Getting Started
Flutter Setup
# Install Flutter on macOS
cd ~/development
unzip ~/Downloads/flutter_macos_3.13.0-stable.zip
export PATH="$PATH:`pwd`/flutter/bin"
# Install Flutter on Windows
C:\src>flutter doctor
# Check installation
flutter doctor
# Create a new project
flutter create my_app
cd my_app
# Run the app
flutter run
cd ~/development
unzip ~/Downloads/flutter_macos_3.13.0-stable.zip
export PATH="$PATH:`pwd`/flutter/bin"
# Install Flutter on Windows
C:\src>flutter doctor
# Check installation
flutter doctor
# Create a new project
flutter create my_app
cd my_app
# Run the app
flutter run
Note: Make sure you have Android Studio/Xcode installed for mobile development or VS Code with Flutter extension.
Basic App Structure
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
Widgets
Basic Widgets
// Text widget
Text(
'Hello, World!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// Button widgets
ElevatedButton(
onPressed: () {
// Handle button press
},
child: Text('Click Me'),
)
TextButton(
onPressed: () {},
child: Text('Text Button'),
)
IconButton(
onPressed: () {},
icon: Icon(Icons.favorite),
)
Text(
'Hello, World!',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// Button widgets
ElevatedButton(
onPressed: () {
// Handle button press
},
child: Text('Click Me'),
)
TextButton(
onPressed: () {},
child: Text('Text Button'),
)
IconButton(
onPressed: () {},
icon: Icon(Icons.favorite),
)
Note: Widgets are the building blocks of Flutter apps. Everything is a widget, from structural elements to styling.
Layout Widgets
// Row and Column
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Left'),
Text('Right'),
],
)
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Top'),
Text('Bottom'),
],
)
// Container with styling
Container(
padding: EdgeInsets.all(16),
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Text('Container with shadow'),
)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Left'),
Text('Right'),
],
)
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Top'),
Text('Bottom'),
],
)
// Container with styling
Container(
padding: EdgeInsets.all(16),
margin: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, 2),
),
],
),
child: Text('Container with shadow'),
)
Note: Layout widgets help you arrange other widgets on the screen. Rows and Columns are the most commonly used layout widgets.
Material Widgets
// AppBar
AppBar(
title: Text('My App'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
),
],
)
// Card
Card(
child: ListTile(
leading: Icon(Icons.album),
title: Text('Card Title'),
subtitle: Text('Subtitle text'),
trailing: Icon(Icons.more_vert),
onTap: () {
// Handle tap
},
),
)
AppBar(
title: Text('My App'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {},
),
],
)
// Card
Card(
child: ListTile(
leading: Icon(Icons.album),
title: Text('Card Title'),
subtitle: Text('Subtitle text'),
trailing: Icon(Icons.more_vert),
onTap: () {
// Handle tap
},
),
)
Custom Widgets
// Stateless Widget
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
CustomButton({required this.text, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(text),
);
}
}
// Usage
CustomButton(
text: 'Custom Button',
onPressed: () {
print('Button pressed');
},
)
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
CustomButton({required this.text, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(text),
);
}
}
// Usage
CustomButton(
text: 'Custom Button',
onPressed: () {
print('Button pressed');
},
)
State Management
Stateful Widget
class CounterWidget extends StatefulWidget {
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
Note: Stateful widgets maintain state that might change during the lifetime of the widget. Use setState() to update the UI when state changes.
Provider Pattern
// Model class
class CounterModel with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// Wrap your app with Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
// Access the model in widgets
Consumer<CounterModel>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
)
class CounterModel with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// Wrap your app with Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
// Access the model in widgets
Consumer<CounterModel>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
)
Note: Provider is a recommended state management solution that makes it easy to manage and access app state across multiple widgets.
Dart Language
Dart Basics
// Variables
var name = 'John'; // Type inferred
String name = 'John'; // Explicit type
final String nickname = 'Johnny'; // Cannot be changed
const String fullName = 'John Doe'; // Compile-time constant
// Functions
void printName(String name) {
print(name);
}
// Arrow function
void printName(String name) => print(name);
// Optional parameters
void printDetails(String name, [int? age]) {
print('Name: $name, Age: $age');
}
// Named parameters
void printPerson({String? name, int? age}) {
print('Name: $name, Age: $age');
}
var name = 'John'; // Type inferred
String name = 'John'; // Explicit type
final String nickname = 'Johnny'; // Cannot be changed
const String fullName = 'John Doe'; // Compile-time constant
// Functions
void printName(String name) {
print(name);
}
// Arrow function
void printName(String name) => print(name);
// Optional parameters
void printDetails(String name, [int? age]) {
print('Name: $name, Age: $age');
}
// Named parameters
void printPerson({String? name, int? age}) {
print('Name: $name, Age: $age');
}
Classes & Async
// Class definition
class Person {
String name;
int age;
Person(this.name, this.age);
// Named constructor
Person.newborn(String name) : this(name, 0);
// Method
void introduce() {
print('Hello, I am $name and I am $age years old');
}
}
// Async programming
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return 'User Data';
}
// Using async/await
void getUserData() async {
try {
String data = await fetchUserData();
print(data);
} catch (e) {
print('Error: $e');
}
}
class Person {
String name;
int age;
Person(this.name, this.age);
// Named constructor
Person.newborn(String name) : this(name, 0);
// Method
void introduce() {
print('Hello, I am $name and I am $age years old');
}
}
// Async programming
Future<String> fetchUserData() async {
await Future.delayed(Duration(seconds: 2));
return 'User Data';
}
// Using async/await
void getUserData() async {
try {
String data = await fetchUserData();
print(data);
} catch (e) {
print('Error: $e');
}
}