💙 Flutter Cheat Sheet
Widgets, navigation, DI, themes, animations — production-ready patterns.
⌨️ Flutter CLI
Essential commands
flutter create my_app # new project flutter create --org com.example my_app # with org flutter run # run on device flutter run -d chrome # web flutter run --release # release mode flutter build apk --split-per-abi # Android flutter build ios # iOS flutter build web # Web flutter pub get # install deps flutter pub upgrade # upgrade deps flutter pub add package_name # add package flutter pub remove package_name # remove flutter clean # clean build flutter doctor # diagnose env flutter devices # list devices flutter analyze # lint check flutter test # run tests
🧩 Core Widgets
StatelessWidget
class MyWidget extends StatelessWidget {
const MyWidget({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Text(title, style: Theme.of(context).textTheme.titleLarge);
}
}StatefulWidget
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
@override
void initState() {
super.initState();
// One-time setup
}
@override
void dispose() {
// Clean up controllers, subscriptions
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text('Count: $_count');
}
}Common widgets quick ref
Text('Hello', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold))
Icon(Icons.home, size: 24, color: Colors.blue)
Image.asset('assets/img.png')
Image.network('https://...')
Image.file(File(path))
ElevatedButton(onPressed: () {}, child: const Text('Click'))
TextButton(onPressed: () {}, child: const Text('Click'))
OutlinedButton(onPressed: () {}, child: const Text('Click'))
IconButton(onPressed: () {}, icon: const Icon(Icons.add))
FloatingActionButton(onPressed: () {}, child: const Icon(Icons.add))
TextField(
controller: _ctrl,
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
prefixIcon: Icon(Icons.email),
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
onSubmitted: (val) => print(val),
)
Switch(value: _on, onChanged: (v) => setState(() => _on = v))
Checkbox(value: _checked, onChanged: (v) => setState(() => _checked = v!))
Slider(value: _val, onChanged: (v) => setState(() => _val = v))Lists & scroll
// Lazy list (recommended for large data)
ListView.builder(
itemCount: items.length,
itemBuilder: (context, i) => ListTile(
leading: CircleAvatar(child: Text('${i+1}')),
title: Text(items[i].name),
subtitle: Text(items[i].email),
trailing: const Icon(Icons.chevron_right),
onTap: () => {},
),
)
// Grid
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: items.length,
itemBuilder: (_, i) => ItemCard(item: items[i]),
)
// Horizontal scroll
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(children: items.map((e) => ItemWidget(e)).toList()),
)📐 Layout
Column & Row
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Top'),
const SizedBox(height: 8),
const Text('Bottom'),
],
)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(Icons.star),
Expanded(child: Text('Title')), // fills remaining space
const Text('Right'),
],
)Stack & Positioned
Stack(
alignment: Alignment.center,
children: [
Container(width: 200, height: 200, color: Colors.blue),
Positioned(
top: 8, right: 8,
child: Icon(Icons.close, color: Colors.white),
),
],
)Container & decoration
Container(
width: double.infinity,
height: 120,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(color: Colors.black26, blurRadius: 8, offset: Offset(0, 4)),
],
gradient: const LinearGradient(
colors: [Colors.blue, Colors.purple],
),
),
child: const Text('Hello'),
)Responsive layout
// MediaQuery
final size = MediaQuery.of(context).size;
final isWide = size.width > 600;
// LayoutBuilder
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return TwoColumnLayout();
}
return SingleColumnLayout();
},
)
// Flexible vs Expanded
Row(children: [
Flexible(flex: 2, child: Container(color: Colors.red)),
Flexible(flex: 1, child: Container(color: Colors.blue)),
])💉 GetIt Dependency Injection
Register dependencies
// service_locator.dart
import 'package:get_it/get_it.dart';
final sl = GetIt.instance;
Future<void> setupLocator() async {
// External
sl.registerLazySingleton<Dio>(() => Dio());
// Data sources
sl.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSourceImpl(sl()),
);
// Repositories
sl.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(remoteDs: sl(), localDs: sl()),
);
// Use cases
sl.registerLazySingleton(() => GetUserUseCase(sl()));
// BLoCs / Cubits (factory = new instance each time)
sl.registerFactory(() => UserBloc(getUserUseCase: sl()));
}
// main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await setupLocator();
runApp(const MyApp());
}
// Usage anywhere
final repo = sl<UserRepository>();
final bloc = sl<UserBloc>();🎨 Themes
Material 3 theme setup
MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
),
textTheme: const TextTheme(
displayLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
bodyMedium: TextStyle(fontSize: 14, height: 1.5),
),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.transparent,
elevation: 0,
),
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
),
themeMode: ThemeMode.system,
)
// Access in widget
Theme.of(context).colorScheme.primary
Theme.of(context).textTheme.titleLarge✨ Animations
AnimatedContainer (implicit)
AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, width: _expanded ? 200 : 100, height: _expanded ? 200 : 100, color: _expanded ? Colors.blue : Colors.red, ) // Other implicit animations AnimatedOpacity(opacity: _visible ? 1.0 : 0.0, duration: ...) AnimatedPadding(padding: ..., duration: ...) AnimatedDefaultTextStyle(style: ..., duration: ...) AnimatedCrossFade( firstChild: WidgetA(), secondChild: WidgetB(), crossFadeState: CrossFadeState.showFirst, duration: ..., )
AnimationController (explicit)
class _MyState extends State<My> with SingleTickerProviderStateMixin {
late final AnimationController _ctrl = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
late final Animation<double> _anim = CurvedAnimation(
parent: _ctrl,
curve: Curves.elasticOut,
);
@override
void dispose() { _ctrl.dispose(); super.dispose(); }
@override
Widget build(BuildContext context) => ScaleTransition(
scale: _anim,
child: const FlutterLogo(size: 100),
);
}
// Play
_ctrl.forward();
_ctrl.reverse();
_ctrl.repeat(reverse: true);Hero animation
// Source widget
Hero(tag: 'avatar-${user.id}', child: CircleAvatar(...))
// Destination widget (same tag)
Hero(tag: 'avatar-${user.id}', child: Image.network(user.avatar))⏳ Async Widgets
FutureBuilder
FutureBuilder<User>(
future: _userFuture, // store future in variable, not inline!
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
}
final user = snapshot.requireData;
return Text(user.name);
},
)StreamBuilder
StreamBuilder<List<Message>>(
stream: chatService.messagesStream,
builder: (context, snapshot) {
if (!snapshot.hasData) return const SizedBox();
final messages = snapshot.requireData;
return ListView.builder(
itemCount: messages.length,
itemBuilder: (_, i) => MessageBubble(messages[i]),
);
},
)📱 Platform & Device
Platform checks
import 'dart:io'; Platform.isIOS // true on iPhone/iPad Platform.isAndroid // true on Android Platform.isMacOS Platform.isWindows // Preferred: Theme Theme.of(context).platform == TargetPlatform.iOS
Safe area & keyboard
// Handle notch / system UI SafeArea(child: YourWidget()) // Avoid keyboard overlap Scaffold( resizeToAvoidBottomInset: true, // default true body: SingleChildScrollView(child: YourForm()), ) // Get keyboard height MediaQuery.of(context).viewInsets.bottom
⚡ Pro Tips
Performance tips
// const constructors prevent rebuilds
const Text('Static text')
const SizedBox(height: 16)
const EdgeInsets.all(16)
// Extract const widgets
static const _gap = SizedBox(height: 16);
// Use keys for list items
ListView.builder(
itemBuilder: (_, i) => ItemWidget(key: ValueKey(items[i].id), item: items[i]),
)
// Avoid expensive builds — cache results
final _expensiveResult = SomeWidget(); // not in build()
// RepaintBoundary for isolated animations
RepaintBoundary(child: AnimatedWidget())Show dialogs & snackbars
// Snackbar
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Saved!')),
);
// Alert dialog
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Confirm'),
content: const Text('Are you sure?'),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('Cancel')),
ElevatedButton(onPressed: () {}, child: const Text('OK')),
],
),
);
// Bottom sheet
showModalBottomSheet(
context: context,
isScrollControlled: true, // full height support
builder: (ctx) => MyBottomSheet(),
);