Flutter Lesson 83 of 83 7 min read
Flutter Cheat Sheet: Widgets and Code Snippets
A one-page Flutter cheat sheet: app skeleton, layout, common widgets, navigation, forms, state, async, theming and CLI commands.
On this page
A compact reference for the code you write most often. Each section links to the full lesson.
App skeleton #
Full lesson: Your first app.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo)),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo, brightness: Brightness.dark),
),
home: const HomePage(),
);
}
}
Stateless and stateful widgets #
Full lessons: StatelessWidget and StatefulWidget.
class Greeting extends StatelessWidget {
const Greeting({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) => Text('Hello, $name');
}
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
void initState() { super.initState(); /* set up */ }
@override
void dispose() { /* clean up */ super.dispose(); }
@override
Widget build(BuildContext context) => TextButton(
onPressed: () => setState(() => _count++),
child: Text('$_count'),
);
}
Layout #
Full lesson: Row and Column.
Column(
mainAxisAlignment: MainAxisAlignment.center, // along the column
crossAxisAlignment: CrossAxisAlignment.start, // across it
mainAxisSize: MainAxisSize.min,
children: [...],
)
Row(children: [const Icon(Icons.star), const SizedBox(width: 8), Expanded(child: Text(title))])
Stack(children: [background, Positioned(right: 8, top: 8, child: badge)])
Padding(padding: const EdgeInsets.all(16), child: child)
const SizedBox(height: 16) // a gap
Center(child: child)
Align(alignment: Alignment.bottomRight, child: child)
Expanded(flex: 2, child: child) // fill a share of the space
const Spacer()
SafeArea(child: child)
ConstrainedBox(constraints: const BoxConstraints(maxWidth: 600), child: child)
AspectRatio(aspectRatio: 16 / 9, child: child)
Wrap(spacing: 8, runSpacing: 8, children: chips)
Container and decoration #
Full lesson: Container.
Container(
width: 200,
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.black12),
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 12, offset: Offset(0, 6))],
),
child: child,
)
Text, images and icons #
Full lessons: Text and Images and icons.
Text('Title', style: Theme.of(context).textTheme.titleLarge)
Text(long, maxLines: 2, overflow: TextOverflow.ellipsis)
Text.rich(TextSpan(text: 'Total ', children: [TextSpan(text: 'Rs. 99', style: bold)]))
Image.asset('assets/images/logo.png', width: 120)
Image.network(url, fit: BoxFit.cover)
const CircleAvatar(radius: 24, backgroundImage: NetworkImage(url))
ClipRRect(borderRadius: BorderRadius.circular(12), child: image)
const Icon(Icons.favorite, color: Colors.red, size: 28)
Buttons #
Full lesson: Buttons.
FilledButton(onPressed: _save, child: const Text('Save'))
FilledButton.tonal(onPressed: _save, child: const Text('Save'))
OutlinedButton(onPressed: _cancel, child: const Text('Cancel'))
TextButton(onPressed: _more, child: const Text('More'))
IconButton(onPressed: _share, icon: const Icon(Icons.share), tooltip: 'Share')
FilledButton.icon(onPressed: _add, icon: const Icon(Icons.add), label: const Text('Add'))
FloatingActionButton(onPressed: _add, child: const Icon(Icons.add))
FilledButton(onPressed: null, child: const Text('Disabled'))
Page structure #
Full lesson: Scaffold and AppBar.
Scaffold(
appBar: AppBar(
title: const Text('Title'),
actions: [IconButton(onPressed: _search, icon: const Icon(Icons.search))],
),
body: body,
floatingActionButton: FloatingActionButton(onPressed: _add, child: const Icon(Icons.add)),
bottomNavigationBar: NavigationBar(
selectedIndex: _index,
onDestinationSelected: (i) => setState(() => _index = i),
destinations: const [
NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
],
),
drawer: const Drawer(child: menu),
)
Lists and grids #
Full lessons: ListView and GridView.
ListView.builder(
itemCount: items.length,
itemBuilder: (context, i) => ListTile(
leading: const Icon(Icons.person),
title: Text(items[i].name),
subtitle: Text(items[i].email),
trailing: const Icon(Icons.chevron_right),
onTap: () => _open(items[i]),
),
)
ListView.separated(
itemCount: items.length,
separatorBuilder: (context, i) => const Divider(height: 1),
itemBuilder: (context, i) => tile(items[i]),
)
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200, mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 0.8,
),
itemCount: items.length,
itemBuilder: (context, i) => card(items[i]),
)
RefreshIndicator(onRefresh: _reload, child: listView)
SingleChildScrollView(padding: const EdgeInsets.all(16), child: column)
Navigation #
Full lessons: Navigator basics and go_router.
Navigator.push(context, MaterialPageRoute<void>(builder: (_) => const DetailsPage()));
Navigator.pop(context);
Navigator.pop(context, result); // return a value
final result = await Navigator.push<String>(context, route); // receive it
Navigator.pushReplacement(context, route);
Navigator.pushAndRemoveUntil(context, route, (r) => false);
// go_router
context.go('/product/42');
context.push('/settings');
context.pop();
Dialogs, sheets and snackbars #
Full lesson: Dialogs, snackbars and sheets.
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Saved')));
final ok = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete?'),
actions: [
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancel')),
FilledButton(onPressed: () => Navigator.pop(context, true), child: const Text('Delete')),
],
),
);
showModalBottomSheet<void>(context: context, showDragHandle: true, builder: (context) => sheet);
Input and forms #
Full lessons: TextField and Forms.
final _controller = TextEditingController(); // dispose it in dispose()
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: TextFormField(
controller: _controller,
decoration: const InputDecoration(labelText: 'Email', border: OutlineInputBorder()),
keyboardType: TextInputType.emailAddress,
validator: (v) => (v == null || !v.contains('@')) ? 'Enter a valid email' : null,
),
)
if (_formKey.currentState!.validate()) { /* submit */ }
Checkbox(value: _on, onChanged: (v) => setState(() => _on = v ?? false))
Switch(value: _on, onChanged: (v) => setState(() => _on = v))
Slider(value: _value, min: 0, max: 100, onChanged: (v) => setState(() => _value = v))
final date = await showDatePicker(
context: context, firstDate: DateTime(2020), lastDate: DateTime(2030),
);
Gestures #
Full lesson: Gestures.
InkWell(onTap: _open, child: child) // with a ripple
GestureDetector(onTap: _open, onLongPress: _menu, child: child)
Dismissible(key: ValueKey(item.id), onDismissed: (_) => _remove(item), child: tile)
State management #
Full lessons: State basics, Provider, Riverpod, Bloc.
// setState
setState(() => _count++);
// ValueNotifier
final counter = ValueNotifier(0);
ValueListenableBuilder<int>(valueListenable: counter, builder: (context, v, _) => Text('$v'))
// ChangeNotifier
class Cart extends ChangeNotifier {
final items = <String>[];
void add(String item) { items.add(item); notifyListeners(); }
}
ListenableBuilder(listenable: cart, builder: (context, _) => Text('${cart.items.length}'))
// Provider
ChangeNotifierProvider(create: (_) => Cart(), child: const MyApp())
context.watch<Cart>().items // in build
context.read<Cart>().add('pen') // in callbacks
// Riverpod
final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);
ref.watch(counterProvider)
ref.read(counterProvider.notifier).increment()
// Cubit
class CounterCubit extends Cubit<int> { CounterCubit() : super(0); void inc() => emit(state + 1); }
BlocBuilder<CounterCubit, int>(builder: (context, count) => Text('$count'))
context.read<CounterCubit>().inc()
Async UI #
Full lessons: FutureBuilder and HTTP requests.
late final Future<List<Post>> _posts = fetchPosts(); // create once, not in build
FutureBuilder<List<Post>>(
future: _posts,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) return const Center(child: Text('Failed to load'));
return ListView(children: [for (final p in snapshot.data!) Text(p.title)]);
},
)
StreamBuilder<int>(stream: _stream, builder: (context, s) => Text('${s.data ?? 0}'))
final response = await http.get(Uri.parse(url)).timeout(const Duration(seconds: 10));
final data = jsonDecode(response.body) as Map<String, dynamic>;
if (!mounted) return; // after an await, before using context or setState
Theme and screen #
Full lessons: Themes and MediaQuery.
final colors = Theme.of(context).colorScheme; // colors.primary, colors.surface
final text = Theme.of(context).textTheme; // text.titleLarge, text.bodyMedium
final size = MediaQuery.sizeOf(context); // size.width, size.height
final keyboard = MediaQuery.viewInsetsOf(context).bottom;
LayoutBuilder(builder: (context, c) => c.maxWidth > 600 ? wide : narrow)
Animation #
Full lessons: Implicit and explicit animations.
AnimatedContainer(duration: const Duration(milliseconds: 300), width: _big ? 200 : 100)
AnimatedOpacity(opacity: _visible ? 1 : 0, duration: const Duration(milliseconds: 200), child: child)
AnimatedSwitcher(duration: const Duration(milliseconds: 250), child: Text('$n', key: ValueKey(n)))
Hero(tag: 'photo-$id', child: image)
late final _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
// with SingleTickerProviderStateMixin; call _controller.dispose() in dispose()
FadeTransition(opacity: _controller, child: child)
Storage #
Full lesson: shared_preferences.
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('darkMode', true);
final dark = prefs.getBool('darkMode') ?? false;
pubspec.yaml #
dependencies:
flutter:
sdk: flutter
http: ^1.2.0
flutter:
uses-material-design: true
assets:
- assets/images/
fonts:
- family: Poppins
fonts:
- asset: assets/fonts/Poppins-Regular.ttf
Command line #
| Command | Does |
|---|---|
flutter doctor | Check the installation |
flutter create my_app | New project |
flutter run | Run in debug mode |
flutter run -d chrome | Run on a chosen device |
flutter run --profile | Run for performance measurement |
flutter pub add <package> | Add a package |
flutter pub get | Download packages |
flutter pub upgrade | Update packages |
flutter analyze | Static analysis |
flutter test | Run tests |
dart format . | Format the code |
dart fix --apply | Apply automatic fixes |
flutter clean | Delete build output |
flutter build apk --split-per-abi | Android APKs |
flutter build appbundle | Android bundle for Google Play |
flutter build ipa | iOS archive for the App Store |
flutter build web | Web build |
flutter upgrade | Update Flutter |
In the terminal while the app runs: r hot reload, R hot restart, q quit.
Common errors #
| Error | Fix |
|---|---|
| RenderFlex overflowed | Expanded, Flexible, or make it scroll |
| Vertical viewport was given unbounded height | Wrap the list in Expanded |
| setState() called after dispose() | if (!mounted) return; after await |
| Could not find the correct Provider | Move the provider above the widget that reads it |
| No Material widget found | Add a Scaffold or Material above |
| Unable to load asset | Check pubspec.yaml indentation and the path, then restart fully |
| MissingPluginException | Stop and run again after adding a plugin |