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
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();
}

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),
)
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'),
)
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
    },
  ),
)

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');
  },
)

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'),
        ),
      ],
    );
  }
}
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}');
  },
)
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');
}

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');
  }
}