Post Composer
Loading preview
Starting the Flutter app
New post with text field, image attachment, character count
import 'package:flutter/material.dart';
class PostComposer extends StatefulWidget {
const PostComposer({super.key});
@override
State<PostComposer> createState() => _PostComposerState();
}
class _PostComposerState extends State<PostComposer> {
final _controller = TextEditingController();
var _hasImage = false;
void _post() {
setState(() {
_controller.clear();
_hasImage = false;
});
_showDemo(context, 'Posted (demo)');
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('New Post'),
actions: [
FilledButton(
onPressed: _controller.text.trim().isEmpty && !_hasImage
? null
: _post,
child: const Text('Post'),
),
],
),
body: Column(
children: [
ListTile(
leading: const CircleAvatar(child: Text('U')),
title: TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'What\'s on your mind?',
border: InputBorder.none,
),
maxLines: 4,
onChanged: (_) => setState(() {}),
),
),
Row(
children: [
IconButton(
tooltip: 'Add image',
icon: const Icon(Icons.image),
onPressed: () => setState(() => _hasImage = !_hasImage),
),
Text(
'${_controller.text.length}/500',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
if (_hasImage)
Container(
margin: const EdgeInsets.all(16),
height: 150,
color: Colors.grey.withValues(alpha: 0.3),
child: const Center(child: Icon(Icons.image, size: 48)),
),
],
),
);
}
}
void _showDemo(BuildContext context, String message) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
}
Add it to your project
Copies the Dart source into your app, so you own it. Flutter SDK only, no extra packages.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
_controller feeds a live 0/500 style counter, kept current by a setState call in onChanged. The limit isn’t enforced, as the field has no maxLength. The image button toggles _hasImage, showing a grey placeholder rather than opening a picker, and Post, disabled while the draft is blank, clears it and shows a demo SnackBar.
Adapting it
- Set
maxLength: 500with a hidden counter, or disablePostpast the limit. - Replace
_hasImagewith a selected file fromimage_picker. - Submit to your API from
_post, clearing the draft only on success.