Flutter Cheatsheet
Flutter - Build Apps for Any Screen
Flutter is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. It is known for its expressive UI, fast development cycle, and native performance.
Table of Contents
- Installation
- Getting Started
- Core Concepts
- Widgets
- Layouts
- Navigation
- State Management
- Networking
- Persistence
- Animations
- Platform Integration
- Testing
- Debugging
- Deployment
- Best Practices
- Troubleshooting
Installation
Flutter SDK
# Download Flutter SDK from https://flutter.dev/docs/get-started/install
# Extract the zip file
# Add Flutter to your path (for the current terminal session)
export PATH="$PATH:`pwd`/flutter/bin"
# Add Flutter to your path permanently (macOS/Linux)
echo 'export PATH="$PATH:[PATH_TO_FLUTTER_GIT_DIRECTORY]/flutter/bin"' >> ~/.zshrc
source ~/.zshrc
# Add Flutter to your path permanently (Windows)
# Update your system's environment variables
Platform Setup
Android Setup
# Install Android Studio
# Download from https://developer.android.com/studio
# Set up Android emulator
# Open Android Studio > AVD Manager > Create Virtual Device
# Accept Android licenses
flutter doctor --android-licenses
iOS Setup (macOS only)
# Install Xcode
# Download from Mac App Store
# Install CocoaPods
sudo gem install cocoapods
# Set up iOS simulator
open -a Simulator
Flutter Doctor
# Check your environment and see a report of the status of your Flutter installation
flutter doctor
# Verbose output
flutter doctor -v
Getting Started
Create a New Project
# Create a new Flutter project
flutter create my_app
# Navigate to the project directory
cd my_app
# Create a project with a specific organization
flutter create --org com.example my_app
# Create a project with a specific template
flutter create -t module my_module
Run the App
# Run the app on an available device
flutter run
# Run on a specific device
flutter run -d chrome
flutter run -d 'iPhone 12'
# Run in release mode
flutter run --release
# Run with hot reload
# Press 'r' in the terminal
# Run with hot restart
# Press 'R' in the terminal
Project Structure
my_app/
├── android/ # Android-specific code
├── ios/ # iOS-specific code
├── lib/ # Main Dart code
│ ├── main.dart # App entry point
│ ├── screens/ # Screen widgets
│ ├── widgets/ # Reusable widgets
│ ├── models/ # Data models
│ ├── services/ # API services
│ └── utils/ # Utility functions
├── test/ # Test files
├── pubspec.yaml # Project dependencies and metadata
└── README.md # Project documentation
Core Concepts
Dart Language Basics
// Variables
var name = 'Flutter';
String language = 'Dart';
int year = 2017;
double version = 2.12;
bool isAwesome = true;
// Functions
int add(int a, int b) {
return a + b;
}
// Arrow syntax
int multiply(int a, int b) => a * b;
// Classes
class Person {
String name;
int age;
Person(this.name, this.age);
void sayHello() {
print('Hello, my name is $name and I am $age years old.');
}
}
// Asynchronous programming
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return 'Data fetched';
}
void main() async {
var person = Person('John Doe', 30);
person.sayHello();
print('Fetching data...');
var data = await fetchData();
print(data);
}
Everything is a Widget
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My First Flutter App'),
),
body: Center(
child: Text('Hello, Flutter!'),
),
),
),
);
}
BuildContext
// BuildContext provides information about the location of a widget in the widget tree.
// It is used to access theme data, navigation, and other inherited widgets.
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Button pressed')),
);
},
child: Text('Press Me'),
);
}
}
Stateless vs. Stateful Widgets
// StatelessWidget: A widget that does not require mutable state.
class MyStatelessWidget extends StatelessWidget {
final String title;
MyStatelessWidget({required this.title});
@override
Widget build(BuildContext context) {
return Text(title);
}
}
// StatefulWidget: A widget that has mutable state.
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $_counter'),
RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
Widgets
Basic Widgets
import 'package:flutter/material.dart';
class BasicWidgets extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Basic Widgets')),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text
Text('This is a Text widget', style: TextStyle(fontSize: 18)),
SizedBox(height: 20),
// Icon
Icon(Icons.favorite, color: Colors.red, size: 30),
SizedBox(height: 20),
// Image
Image.network('https://flutter.dev/images/flutter-logo-sharing.png'),
SizedBox(height: 20),
// Asset Image
Image.asset('assets/images/logo.png'),
SizedBox(height: 20),
// Button
RaisedButton(
onPressed: () {},
child: Text('RaisedButton'),
),
SizedBox(height: 10),
FlatButton(
onPressed: () {},
child: Text('FlatButton'),
),
SizedBox(height: 10),
IconButton(
onPressed: () {},
icon: Icon(Icons.thumb_up),
),
SizedBox(height: 20),
// TextField
TextField(
decoration: InputDecoration(
labelText: 'Enter your name',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
// Checkbox
CheckboxListTile(
title: Text('Accept terms and conditions'),
value: true,
onChanged: (value) {},
),
SizedBox(height: 20),
// Radio
RadioListTile(
title: Text('Option 1'),
value: 1,
groupValue: 1,
onChanged: (value) {},
),
SizedBox(height: 20),
// Switch
SwitchListTile(
title: Text('Enable notifications'),
value: true,
onChanged: (value) {},
),
SizedBox(height: 20),
// Slider
Slider(
value: 0.5,
onChanged: (value) {},
),
SizedBox(height: 20),
// Progress Indicator
CircularProgressIndicator(),
SizedBox(height: 10),
LinearProgressIndicator(),
],
),
),
);
}
}
Material Components
import 'package:flutter/material.dart';
class MaterialComponents extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Material Components')),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(child: Text('Drawer Header')),
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Card
Card(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('This is a Card'),
),
),
SizedBox(height: 20),
// Chip
Chip(
avatar: CircleAvatar(child: Text('A')),
label: Text('Chip'),
),
SizedBox(height: 20),
// DataTable
DataTable(
columns: [
DataColumn(label: Text('ID')),
DataColumn(label: Text('Name')),
],
rows: [
DataRow(cells: [DataCell(Text('1')), DataCell(Text('John'))]),
DataRow(cells: [DataCell(Text('2')), DataCell(Text('Jane'))]),
],
),
SizedBox(height: 20),
// SnackBar
RaisedButton(
onPressed: () {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('This is a SnackBar')),
);
},
child: Text('Show SnackBar'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
);
}
}
Layouts
Single-Child Layout Widgets
import 'package:flutter/material.dart';
class SingleChildLayouts extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Single-Child Layouts')),
body: Column(
children: [
// Container
Container(
color: Colors.blue,
padding: EdgeInsets.all(16.0),
margin: EdgeInsets.all(16.0),
child: Text('Container'),
),
// Center
Center(
child: Text('Center'),
),
// Padding
Padding(
padding: EdgeInsets.all(16.0),
child: Text('Padding'),
),
// Align
Align(
alignment: Alignment.bottomRight,
child: Text('Align'),
),
// SizedBox
SizedBox(
width: 200,
height: 100,
child: Card(child: Center(child: Text('SizedBox'))),
),
// AspectRatio
AspectRatio(
aspectRatio: 16 / 9,
child: Container(color: Colors.green),
),
// ConstrainedBox
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200, maxHeight: 100),
child: Container(color: Colors.red),
),
],
),
);
}
}
Multi-Child Layout Widgets
import 'package:flutter/material.dart';
class MultiChildLayouts extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Multi-Child Layouts')),
body: Column(
children: [
// Row
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [Text('Row 1'), Text('Row 2'), Text('Row 3')],
),
// Column
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text('Column 1'), Text('Column 2'), Text('Column 3')],
),
// Stack
Stack(
children: [
Container(width: 100, height: 100, color: Colors.red),
Container(width: 80, height: 80, color: Colors.green),
Container(width: 60, height: 60, color: Colors.blue),
],
),
// ListView
Expanded(
child: ListView(
children: [
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
ListTile(title: Text('Item 3')),
],
),
),
// GridView
Expanded(
child: GridView.count(
crossAxisCount: 2,
children: List.generate(4, (index) => Center(child: Text('Item $index'))),
),
),
],
),
);
}
}
Navigation
Navigator 1.0 (Imperative)
// Navigate to a new screen
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsScreen()),
);
// Navigate back
Navigator.pop(context);
// Navigate with data
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(item: item),
),
);
// Receive data from a screen
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
// Send data back
Navigator.pop(context, 'Yes');
Navigator 2.0 (Declarative)
// pubspec.yaml
dependencies:
flutter:
sdk: flutter
go_router: ^3.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
);
}
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/details/:id',
builder: (context, state) => DetailsScreen(id: state.params['id']!),
),
],
);
}
// Navigate with go_router
context.go('/details/123');
context.push('/details/456');
State Management
Provider
// pubspec.yaml
dependencies:
provider: ^6.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// Usage in a widget
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Center(
child: Text('Count: ${context.watch<Counter>().count}'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<Counter>().increment(),
child: Icon(Icons.add),
),
);
}
}
BLoC (Business Logic Component)
// pubspec.yaml
dependencies:
flutter_bloc: ^8.0.0
// counter_event.dart
abstract class CounterEvent {}
class Increment extends CounterEvent {}
// counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'counter_event.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
}
}
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(
BlocProvider(
create: (context) => CounterBloc(),
child: MyApp(),
),
);
}
// Usage in a widget
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BLoC Example')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, count) => Text('Count: $count'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(Increment()),
child: Icon(Icons.add),
),
);
}
}
Riverpod
// pubspec.yaml
dependencies:
flutter_riverpod: ^1.0.0
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateProvider((ref) => 0);
void main() {
runApp(ProviderScope(child: MyApp()));
}
// Usage in a widget
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text('Riverpod Example')),
body: Center(
child: Text('Count: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.state).state++,
child: Icon(Icons.add),
),
);
}
}
Networking
http Package
// pubspec.yaml
dependencies:
http: ^0.13.0
// services/api_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiService {
Future<Map<String, dynamic>> fetchData() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load data');
}
}
}
dio Package
// pubspec.yaml
dependencies:
dio: ^4.0.0
// services/api_service.dart
import 'package:dio/dio.dart';
class ApiService {
final dio = Dio();
Future<Map<String, dynamic>> fetchData() async {
try {
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
return response.data;
} catch (e) {
throw Exception('Failed to load data');
}
}
}
Persistence
shared_preferences
// pubspec.yaml
dependencies:
shared_preferences: ^2.0.0
// utils/storage.dart
import 'package:shared_preferences/shared_preferences.dart';
class Storage {
Future<void> saveUsername(String username) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', username);
}
Future<String?> getUsername() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('username');
}
}
sqflite
// pubspec.yaml
dependencies:
sqflite: ^2.0.0
path: ^1.8.0
// services/database_service.dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseService {
late Database _database;
Future<void> initDatabase() async {
_database = await openDatabase(
join(await getDatabasesPath(), 'my_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',
);
},
version: 1,
);
}
Future<void> insertUser(Map<String, dynamic> user) async {
await _database.insert('users', user, conflictAlgorithm: ConflictAlgorithm.replace);
}
Future<List<Map<String, dynamic>>> getUsers() async {
return await _database.query('users');
}
}
Animations
Implicit Animations
class ImplicitAnimation extends StatefulWidget {
@override
_ImplicitAnimationState createState() => _ImplicitAnimationState();
}
class _ImplicitAnimationState extends State<ImplicitAnimation> {
bool _isBig = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _isBig = !_isBig),
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _isBig ? 200 : 100,
height: _isBig ? 200 : 100,
color: _isBig ? Colors.blue : Colors.red,
),
);
}
}
Explicit Animations
class ExplicitAnimation extends StatefulWidget {
@override
_ExplicitAnimationState createState() => _ExplicitAnimationState();
}
class _ExplicitAnimationState extends State<ExplicitAnimation> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
_animation = Tween<double>(begin: 0, end: 300).animate(_controller)
..addListener(() => setState(() {}));
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.green,
);
}
}
Platform Integration
MethodChannel
// Dart side
import 'package:flutter/services.dart';
const platform = MethodChannel('com.example.myapp/channel');
Future<String> getBatteryLevel() async {
try {
final int result = await platform.invokeMethod('getBatteryLevel');
return 'Battery level at $result % .';
} on PlatformException catch (e) {
return "Failed to get battery level: '${e.message}'.";
}
}
// Android side (MainActivity.java)
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example.myapp/channel";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getBatteryLevel")) {
// ... get battery level
result.success(batteryLevel);
}
}
);
}
}
Testing
Unit Testing
// test/counter_test.dart
import 'package:test/test.dart';
import 'package:my_app/counter.dart';
void main() {
test('Counter value should be incremented', () {
final counter = Counter();
counter.increment();
expect(counter.value, 1);
});
}
Widget Testing
// test/widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
Integration Testing
// test_driver/app_test.dart
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('Counter App', () {
FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null) {
driver.close();
}
});
test('increments the counter', () async {
final counterTextFinder = find.byValueKey('counter');
final buttonFinder = find.byValueKey('increment');
expect(await driver.getText(counterTextFinder), '0');
await driver.tap(buttonFinder);
expect(await driver.getText(counterTextFinder), '1');
});
});
}
Debugging
DevTools
# Open DevTools from the terminal
flutter pub global activate devtools
flutter pub global run devtools
# Or from VS Code / Android Studio
Logging
import 'dart:developer' as developer;
void myFunction() {
developer.log('This is a log message', name: 'my.app.category');
}
Deployment
Android
# Build an APK
flutter build apk
# Build an App Bundle
flutter build appbundle
iOS
# Build an IPA
flutter build ipa
Web
# Build for web
flutter build web
Best Practices
- Follow Effective Dart guidelines: Write clean, maintainable Dart code.
- Organize your project structure: Keep your code organized and easy to navigate.
- Use a state management solution: Choose a state management solution that fits your app's complexity.
- Write tests: Write unit, widget, and integration tests to ensure your app is working correctly.
- Optimize performance: Use DevTools to identify and fix performance bottlenecks.
- Handle errors gracefully: Use try-catch blocks and error widgets to handle errors.
Troubleshooting
Common Issues
- Platform-specific issues: Check your platform setup (Android Studio, Xcode).
- Dependency conflicts: Run
flutter pub deps
to check for dependency conflicts. - Build errors: Run
flutter clean
and thenflutter pub get
to clean up your build files.
Summary
Flutter is a powerful and flexible UI toolkit for building high-quality, cross-platform applications. Its key features include:
- Fast Development: Hot reload allows you to see changes instantly.
- Expressive UI: Build beautiful and custom UIs with a rich set of widgets.
- Native Performance: Flutter apps are compiled to native code, providing excellent performance.
- Single Codebase: Write once, run on mobile, web, and desktop.
- Growing Community: A large and active community provides support and a rich ecosystem of packages.
Flutter is an excellent choice for developers who want to build beautiful, high-performance applications for multiple platforms with a single codebase.