Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Flutter Development: A Comprehensive Introduction

Tech May 9 3

Flutter Overview

Flutter is an open-source UI framework created by Google for building natively compiled applications for mobile, web, and desktop from a single codebase. It uses the Dart language and provides a rich set of pre-designed widgets. Flutter's own rendering engine draws every pixel on the screen, offering high performance and a consistent look across platforms.

Key features:

  • Cross-platform dveelopment with a single codebase
  • High-performance randering via a custom engine
  • Extensive widget library with material design and Cupertino styles

Environment Setup

  1. Download the Flutter SDK from the official archive or GitHub releases.
  2. Extract the archive and add the flutter/bin directory to your system PATH.
  3. Run flutter doctor to verify that all dependencies are installed.

Create your first project:

  • Install the Flutter plugin for Android Studio.
  • Use Android Studio to create a new Flutter project.

Dart SDK Setup

  • Download the Dart SDK from the official website or the Windows archive.

Dart Language Basics

Variables and Constants

void main() {
  var username = "Alice";
  username = "Bob";

  dynamic temp = "test";
  Object obj = "test";

  final password = "secret";
  // password = "newsecret"; // Error

  const apiKey = "12345";
  // apiKey = "67890"; // Error
}

Data Types

void main() {
  // Numbers
  int a = 10;
  int parsedInt = int.parse('42');
  double b = 3.14;
  double parsedDouble = double.parse('2.718');

  // Strings
  String fromInt = 123.toString();
  String formatted = 3.14159.toStringAsFixed(2);

  // Booleans
  bool compare = 1 == '1'; // false
  bool greater = 5 > 3;    // true

  // Lists
  List<int> numbers = [1, 3, 5, 7, 9];
  // List<int> more = List(); // Deprecated
  // more.add(2);
  // more.addAll([4, 6, 8, 10]);

  // Maps
  Map<String, int> point = {'x': 1, 'y': 2};
  // Map<String, int> other = Map(); // Deprecated
  // other['x'] = 1;
  print(point.containsKey('x')); // true
  point.remove('y');
  print(point); // {x: 1}
}

Functions

void main() {
  print(getUserName());
  print(getPersonInfo(111));
  print(addAge(10));
  print(addAge2(age1: 20, age2: 5));

  var items = ["alpha", "beta", "gamma"];
  items.forEach((item) {
    print(item);
  });
}

String getUserName() => "Hello World";

String getPersonInfo(int userId) {
  Map<int, String> users = {111: "Alice", 222: "Bob"};
  return users[userId] ?? "Unknown";
}

int addAge(int age1, [int age2 = 0]) => age1 + age2;

int addAge2({int age1 = 0, int age2 = 0}) => age1 + age2;

Classes and Inheritance

void main() {
  var person = Person(25, "Charlie");
  print(person.age);
  person.sayHello();

  var worker = Worker(30, "Dave", 5000);
  worker.sayHello();
}

class Person {
  int age;
  String name;

  Person(this.age, this.name);

  void sayHello() {
    print("My name is $name");
  }
}

class Worker extends Person {
  int salary;

  Worker(int age, String name, this.salary) : super(age, name);

  @override
  void sayHello() {
    super.sayHello();
    print("My salary is $salary");
  }
}

Mixins and Abstract Classes

Dart does not support multiple inheritance. Mixins allow code reuse across class hierarchies.

void main() {
  var person = Person(20, "Eve");
  person.eat();
  person.sleep();
  person.haveABaby();
}

mixin Eat {
  void eat() => print("Eating...");
}

mixin Sleep {
  void sleep() => print("Sleeping...");
}

abstract class Animal {
  void haveABaby();
}

class Person extends Animal with Eat, Sleep {
  int age;
  String name;

  Person(this.age, this.name);

  void sayHello() => print("Hi, I'm $name");

  @override
  void haveABaby() => print("Having a baby");
}

Libraries and Packages

import 'pkg/Calculator.dart';
import 'package:http/http.dart' as http;
import 'dart:math' deferred as math;

void main() async {
  var calc = Calculator(10, 5);
  calc.subtract();

  await math.loadLibrary();
  var random = math.Random();
  print(random.nextInt(10));
}

Asynchronous Programming

void main() async {
  print("Start");
  await Future.delayed(Duration(seconds: 2), () => print("Async task"));
  print("End");

  // Wait for multiple futures
  await Future.wait([
    Future.delayed(Duration(seconds: 1), () => print("First")),
    Future.delayed(Duration(seconds: 3), () => print("Second")),
    Future.delayed(Duration(seconds: 2), () => print("Third")),
  ]);
  print("All done");
}

Widgets

Text and Styling

@override
Widget build(BuildContext context) {
  const customStyle = TextStyle(
    color: Color(0xFFFF0000),
    fontSize: 20,
    fontFamily: 'Roboto',
    decoration: TextDecoration.underline,
    decorationStyle: TextDecorationStyle.dashed,
  );

  return Scaffold(
    appBar: AppBar(title: Text('Text Demo')),
    body: Column(
      children: [
        Text(
          'Long text that will be truncated with ellipsis',
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
          textAlign: TextAlign.center,
          textScaleFactor: 1.5,
        ),
        Text('Styled text', style: customStyle),
        Text.rich(
          TextSpan(
            children: [
              TextSpan(text: 'Visit ', style: TextStyle(fontSize: 18)),
              TextSpan(
                text: 'our website',
                style: TextStyle(color: Colors.blue, fontSize: 20),
              ),
            ],
          ),
        ),
      ],
    ),
  );
}

Buttons

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Buttons')),
    body: Column(
      children: [
        ElevatedButton(
          onPressed: () => print('Elevated pressed'),
          child: Text('Elevated'),
        ),
        TextButton(
          onPressed: () => print('Text pressed'),
          child: Text('Text'),
        ),
        OutlinedButton(
          onPressed: () => print('Outlined pressed'),
          child: Text('Outlined'),
        ),
        TextButton(
          onPressed: () {},
          style: ButtonStyle(
            backgroundColor: MaterialStateProperty.all(Colors.blue),
            shape: MaterialStateProperty.all(
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
            ),
          ),
          child: Text('Custom'),
        ),
      ],
    ),
  );
}

Images and Icons

Local Assets

Image.asset('assets/images/logo.png', width: 200)

Network Images

Image.network(
  'https://example.com/image.png',
  width: 150,
  fit: BoxFit.cover,
  color: Colors.pink,
  colorBlendMode: BlendMode.difference,
)

Custom Icon Font

  1. Add the font file to your project's assets and declare it in pubspec.yaml.
  2. Create a custom icon class:
import 'package:flutter/material.dart';

class MyIcons {
  static const IconData home = IconData(0xe900, fontFamily: 'MyCustomIcons');
  static const IconData settings = IconData(0xe901, fontFamily: 'MyCustomIcons');
}
  1. Use it:
Icon(MyIcons.home, color: Colors.amber);

Dropdown Button

class CitySelector extends StatefulWidget {
  @override
  _CitySelectorState createState() => _CitySelectorState();
}

class _CitySelectorState extends State<CitySelector> {
  String? _selectedCity;

  List<DropdownMenuItem<String>> _buildCityMenu() {
    return ['Shanghai', 'Beijing', 'Guangzhou', 'Shenzhen']
        .map((city) => DropdownMenuItem(value: city, child: Text(city)))
        .toList();
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButton<String>(
      items: _buildCityMenu(),
      hint: Text('Select a city'),
      value: _selectedCity,
      onChanged: (value) {
        setState(() => _selectedCity = value);
      },
    );
  }
}

Radio and Checkbox

Single Seelction (Radio)

class FruitSelector extends StatefulWidget {
  @override
  _FruitSelectorState createState() => _FruitSelectorState();
}

class _FruitSelectorState extends State<FruitSelector> {
  String? _selectedFruit;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        RadioListTile<String>(
          title: Text('Apple'),
          value: 'apple',
          groupValue: _selectedFruit,
          onChanged: (value) => setState(() => _selectedFruit = value),
        ),
        RadioListTile<String>(
          title: Text('Banana'),
          value: 'banana',
          groupValue: _selectedFruit,
          onChanged: (value) => setState(() => _selectedFruit = value),
        ),
      ],
    );
  }
}

Multiple Selection (Checkbox)

class HobbySelector extends StatefulWidget {
  @override
  _HobbySelectorState createState() => _HobbySelectorState();
}

class _HobbySelectorState extends State<HobbySelector> {
  List<String> _selectedHobbies = [];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CheckboxListTile(
          title: Text('Reading'),
          value: _selectedHobbies.contains('reading'),
          onChanged: (value) {
            setState(() {
              value
                  ? _selectedHobbies.add('reading')
                  : _selectedHobbies.remove('reading');
            });
          },
        ),
        CheckboxListTile(
          title: Text('Coding'),
          value: _selectedHobbies.contains('coding'),
          onChanged: (value) {
            setState(() {
              value
                  ? _selectedHobbies.add('coding')
                  : _selectedHobbies.remove('coding');
            });
          },
        ),
      ],
    );
  }
}

Text Input

class TextInputExample extends StatefulWidget {
  @override
  _TextInputExampleState createState() => _TextInputExampleState();
}

class _TextInputExampleState extends State<TextInputExample> {
  final TextEditingController _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(16),
      child: TextField(
        controller: _controller,
        decoration: InputDecoration(
          labelText: 'Type something',
          border: OutlineInputBorder(),
        ),
      ),
    );
  }
}

Layouts

Linear Layout (Row & Column)

// Row
Row(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Container(width: 100, height: 200, color: Colors.red),
    Container(width: 100, height: 300, color: Colors.blue),
  ],
)

// Column
Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Container(width: 100, height: 200, color: Colors.red),
    Container(width: 100, height: 300, color: Colors.blue),
  ],
)

Flex Layout

Flex(
  direction: Axis.horizontal,
  children: [
    Container(width: 50, height: 300, color: Colors.black),
    Expanded(flex: 1, child: Container(height: 300, color: Colors.red)),
    Expanded(flex: 1, child: Container(height: 300, color: Colors.blue)),
    Expanded(flex: 2, child: Container(height: 300, color: Colors.yellow)),
  ],
)

Stack and Positioned

SizedBox(
  height: 400,
  width: 400,
  child: Stack(
    alignment: Alignment.topRight,
    children: [
      Positioned(
        left: 15,
        top: 30,
        child: Container(width: 100, height: 100, color: Colors.red),
      ),
      Positioned(
        right: 50,
        top: 80,
        child: Container(width: 200, height: 200, color: Colors.blue),
      ),
    ],
  ),
)

Wrap Layout

Wrap(
  children: [
    Container(width: 150, height: 150, color: Colors.red),
    Container(width: 150, height: 150, color: Colors.blue),
    Container(width: 150, height: 150, color: Colors.green),
  ],
)

Containers

Padding

Container(
  width: 400,
  height: 400,
  color: Colors.red,
  child: Padding(
    padding: EdgeInsets.only(top: 100),
    child: Container(color: Colors.blue),
  ),
)

ConstrainedBox

ConstrainedBox(
  constraints: BoxConstraints(
    minWidth: double.infinity,
    minHeight: 200,
    maxHeight: 300,
  ),
  child: Container(width: 1, color: Colors.red),
)

DecoratedBox

DecoratedBox(
  decoration: BoxDecoration(
    gradient: LinearGradient(colors: [Colors.yellow, Colors.red]),
    borderRadius: BorderRadius.circular(8),
    boxShadow: [
      BoxShadow(color: Colors.black, offset: Offset(3, 3), blurRadius: 4),
    ],
  ),
  child: TextButton(onPressed: () {}, child: Text('Styled Button')),
)

Transform

// Translation
Transform.translate(
  offset: Offset(50, 10),
  child: Text('Moved text'),
)

// Rotation
Transform.rotate(
  angle: pi / 2,
  child: Text('Rotated text'),
)

// Scale
Transform.scale(
  scale: 2,
  child: Text('Scaled text'),
)

Container

Column(
  children: [
    Container(
      margin: EdgeInsets.only(bottom: 20),
      color: Colors.blue,
      height: 200,
      width: 300,
      alignment: Alignment.centerRight,
      child: Text('Box 1'),
    ),
    Container(
      margin: EdgeInsets.only(top: 20),
      color: Colors.red,
      height: 200,
      width: 300,
      alignment: Alignment.centerRight,
      child: Text('Box 2'),
    ),
  ],
)

Scrollable Widgets

// SingleChildScrollView
Scrollbar(
  child: SingleChildScrollView(
    child: Container(height: 3000, color: Colors.red),
  ),
)

// ListView.builder
Container(
  height: 400,
  child: ListView.builder(
    itemCount: 50,
    itemExtent: 50,
    itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
  ),
)

// GridView
GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 4,
    childAspectRatio: 1,
  ),
  children: List.generate(9, (i) => Center(child: Text('${i+1}'))),
)

Scaffold Structure

AppBar and Bottom Navigation

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _currentIndex = 1;
  final List<Widget> _pages = [PageOne(), PageTwo(), PageThree()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Main App'),
        actions: [
          IconButton(
            icon: Icon(Icons.settings),
            onPressed: () => print('Settings pressed'),
          ),
        ],
      ),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () => print('FAB pressed'),
      ),
    );
  }
}

TabBar

class TabbedPage extends StatefulWidget {
  @override
  _TabbedPageState createState() => _TabbedPageState();
}

class _TabbedPageState extends State<TabbedPage> with SingleTickerProviderStateMixin {
  final List<String> _tabs = ['Tab A', 'Tab B', 'Tab C'];
  late TabController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TabController(length: _tabs.length, vsync: this);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
          color: Colors.pink,
          child: TabBar(
            controller: _controller,
            tabs: _tabs.map((t) => Tab(text: t)).toList(),
          ),
        ),
        Expanded(
          child: TabBarView(
            controller: _controller,
            children: _tabs.map((t) => Center(child: Text('$t content'))).toList(),
          ),
        ),
      ],
    );
  }
}

Drawer

Scaffold(
  key: _scaffoldKey,
  appBar: AppBar(
    title: Text('Drawer Demo'),
    leading: IconButton(
      icon: Icon(Icons.menu),
      onPressed: () => _scaffoldKey.currentState?.openDrawer(),
    ),
  ),
  drawer: Drawer(
    child: Container(
      width: 300,
      color: Colors.pink,
      child: Column(
        children: [Text('Drawer content')],
      ),
    ),
  ),
  body: Center(child: Text('Main content')),
)

Events

Pointer Events

Listener(
  onPointerDown: (event) => print('Down'),
  onPointerUp: (event) => print('Up'),
  onPointerMove: (event) => print('Move'),
  onPointerCancel: (event) => print('Cancel'),
  child: Container(height: 200, width: 200, color: Colors.red),
)

Gesture Events

GestureDetector(
  onTap: () => print('Tap'),
  onDoubleTap: () => print('Double tap'),
  onLongPress: () => print('Long press'),
  onHorizontalDragStart: (details) => print('Horizontal drag start'),
  onScaleUpdate: (details) => print('Scale update'),
  child: Container(width: 200, height: 200, color: Colors.blue),
)

HTTP Requests

import 'package:dio/dio.dart';

class NetworkPage extends StatefulWidget {
  @override
  _NetworkPageState createState() => _NetworkPageState();
}

class _NetworkPageState extends State<NetworkPage> {
  String _responseData = "";

  Future<void> fetchData() async {
    try {
      var response = await Dio().get(
        'https://api.example.com/data',
        queryParameters: {'key': 'value'},
      );
      setState(() => _responseData = response.data.toString());
    } catch (e) {
      print('Error: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: fetchData,
          child: Text('Fetch Data'),
          style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
        ),
        Scrollbar(
          child: Container(
            height: 400,
            child: SingleChildScrollView(
              child: Text(_responseData),
            ),
          ),
        ),
      ],
    );
  }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.