💙 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(),
);