Dart Tutorial

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)

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 #

CommandDoes
flutter doctorCheck the installation
flutter create my_appNew project
flutter runRun in debug mode
flutter run -d chromeRun on a chosen device
flutter run --profileRun for performance measurement
flutter pub add <package>Add a package
flutter pub getDownload packages
flutter pub upgradeUpdate packages
flutter analyzeStatic analysis
flutter testRun tests
dart format .Format the code
dart fix --applyApply automatic fixes
flutter cleanDelete build output
flutter build apk --split-per-abiAndroid APKs
flutter build appbundleAndroid bundle for Google Play
flutter build ipaiOS archive for the App Store
flutter build webWeb build
flutter upgradeUpdate Flutter

In the terminal while the app runs: r hot reload, R hot restart, q quit.

Common errors #

ErrorFix
RenderFlex overflowedExpanded, Flexible, or make it scroll
Vertical viewport was given unbounded heightWrap the list in Expanded
setState() called after dispose()if (!mounted) return; after await
Could not find the correct ProviderMove the provider above the widget that reads it
No Material widget foundAdd a Scaffold or Material above
Unable to load assetCheck pubspec.yaml indentation and the path, then restart fully
MissingPluginExceptionStop and run again after adding a plugin
Practise in the playground Updated by Santosh Adhikari