Skip to content

Flutter 开发指南

简介

Flutter 是 Google 开发的开源 UI 框架,用于构建跨平台的移动应用。它使用 Dart 语言,提供了丰富的组件库和工具,可以快速构建美观、高性能的应用程序。本文将介绍 Flutter 的核心概念、开发流程和最佳实践。

环境搭建

1. 开发环境配置

bash
# 安装 Flutter SDK
# 从 https://flutter.dev/docs/get-started/install 下载并解压

# 配置环境变量
export PATH="$PATH:`pwd`/flutter/bin"

# 安装依赖
flutter doctor

# 安装 Android Studio 和 Xcode
# 从官网下载并安装

2. 创建项目

bash
# 创建新项目
flutter create my_app

# 运行项目
cd my_app
flutter run

基础组件

1. 核心组件

dart
import 'package:flutter/material.dart';

class BasicComponents extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('基础组件示例'),
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 文本组件
            Text(
              '这是一段文本',
              style: TextStyle(
                fontSize: 16.0,
                color: Colors.black,
              ),
            ),
            SizedBox(height: 16.0),

            // 图片组件
            Image.network(
              'https://example.com/image.jpg',
              width: double.infinity,
              height: 200.0,
              fit: BoxFit.cover,
            ),
            SizedBox(height: 16.0),

            // 输入框组件
            TextField(
              decoration: InputDecoration(
                hintText: '请输入内容',
                border: OutlineInputBorder(),
              ),
              onChanged: (value) {
                print('输入内容: $value');
              },
            ),
            SizedBox(height: 16.0),

            // 按钮组件
            ElevatedButton(
              onPressed: () {
                print('按钮被点击');
              },
              child: Text('点击我'),
            ),
          ],
        ),
      ),
    );
  }
}

2. 列表组件

dart
import 'package:flutter/material.dart';

class ListExample extends StatelessWidget {
  final List<String> items = List.generate(20, (index) => '项目 ${index + 1}');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('列表示例'),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(
            leading: Icon(Icons.star),
            title: Text(items[index]),
            subtitle: Text('这是第 ${index + 1} 个项目'),
            trailing: Icon(Icons.arrow_forward_ios),
            onTap: () {
              print('点击了 ${items[index]}');
            },
          );
        },
      ),
    );
  }
}

// 网格列表
class GridExample extends StatelessWidget {
  final List<String> items = List.generate(20, (index) => '项目 ${index + 1}');

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('网格列表示例'),
      ),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          crossAxisSpacing: 10.0,
          mainAxisSpacing: 10.0,
        ),
        itemCount: items.length,
        itemBuilder: (context, index) {
          return Card(
            child: Center(
              child: Text(items[index]),
            ),
          );
        },
      ),
    );
  }
}

状态管理

1. Provider

dart
// 定义数据模型
class User {
  final String name;
  final int age;

  User({this.name, this.age});
}

// 定义 Provider
class UserProvider with ChangeNotifier {
  User _user;

  User get user => _user;

  void updateUser(User user) {
    _user = user;
    notifyListeners();
  }
}

// 使用 Provider
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => UserProvider(),
      child: MyApp(),
    ),
  );
}

class UserProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<UserProvider>(
      builder: (context, userProvider, child) {
        final user = userProvider.user;
        return Column(
          children: [
            Text('姓名: ${user?.name ?? "未设置"}'),
            Text('年龄: ${user?.age ?? 0}'),
            ElevatedButton(
              onPressed: () {
                userProvider.updateUser(User(name: '张三', age: 25));
              },
              child: Text('更新用户信息'),
            ),
          ],
        );
      },
    );
  }
}

2. Bloc

dart
// 定义事件
abstract class UserEvent {}

class UpdateUser extends UserEvent {
  final User user;
  UpdateUser(this.user);
}

// 定义状态
class UserState {
  final User user;
  UserState(this.user);
}

// 定义 Bloc
class UserBloc extends Bloc<UserEvent, UserState> {
  UserBloc() : super(UserState(null));

  @override
  Stream<UserState> mapEventToState(UserEvent event) async* {
    if (event is UpdateUser) {
      yield UserState(event.user);
    }
  }
}

// 使用 Bloc
class UserProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<UserBloc, UserState>(
      builder: (context, state) {
        return Column(
          children: [
            Text('姓名: ${state.user?.name ?? "未设置"}'),
            Text('年龄: ${state.user?.age ?? 0}'),
            ElevatedButton(
              onPressed: () {
                context.read<UserBloc>().add(
                  UpdateUser(User(name: '张三', age: 25)),
                );
              },
              child: Text('更新用户信息'),
            ),
          ],
        );
      },
    );
  }
}

导航

1. 基本导航

dart
class NavigationExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
      routes: {
        '/detail': (context) => DetailScreen(),
        '/settings': (context) => SettingsScreen(),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('首页'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/detail');
              },
              child: Text('跳转到详情页'),
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/settings');
              },
              child: Text('跳转到设置页'),
            ),
          ],
        ),
      ),
    );
  }
}

2. 命名路由

dart
class AppRoutes {
  static const String home = '/';
  static const String detail = '/detail';
  static const String settings = '/settings';
}

class AppRouter {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case AppRoutes.home:
        return MaterialPageRoute(builder: (_) => HomeScreen());
      case AppRoutes.detail:
        return MaterialPageRoute(builder: (_) => DetailScreen());
      case AppRoutes.settings:
        return MaterialPageRoute(builder: (_) => SettingsScreen());
      default:
        return MaterialPageRoute(
          builder: (_) => Scaffold(
            body: Center(
              child: Text('未找到路由: ${settings.name}'),
            ),
          ),
        );
    }
  }
}

网络请求

1. HTTP 请求

dart
import 'package:http/http.dart' as http;
import 'dart:convert';

class ApiService {
  static const String baseUrl = 'https://api.example.com';

  Future<List<dynamic>> getList() async {
    final response = await http.get(Uri.parse('$baseUrl/list'));
    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('加载失败');
    }
  }

  Future<Map<String, dynamic>> getDetail(int id) async {
    final response = await http.get(Uri.parse('$baseUrl/detail/$id'));
    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception('加载失败');
    }
  }

  Future<void> postData(Map<String, dynamic> data) async {
    final response = await http.post(
      Uri.parse('$baseUrl/submit'),
      headers: {'Content-Type': 'application/json'},
      body: json.encode(data),
    );
    if (response.statusCode != 200) {
      throw Exception('提交失败');
    }
  }
}

2. 使用 Dio

dart
import 'package:dio/dio.dart';

class DioService {
  final Dio _dio = Dio(BaseOptions(
    baseUrl: 'https://api.example.com',
    connectTimeout: 5000,
    receiveTimeout: 3000,
  ));

  Future<List<dynamic>> getList() async {
    try {
      final response = await _dio.get('/list');
      return response.data;
    } on DioError catch (e) {
      throw Exception('加载失败: ${e.message}');
    }
  }

  Future<Map<String, dynamic>> getDetail(int id) async {
    try {
      final response = await _dio.get('/detail/$id');
      return response.data;
    } on DioError catch (e) {
      throw Exception('加载失败: ${e.message}');
    }
  }

  Future<void> postData(Map<String, dynamic> data) async {
    try {
      await _dio.post('/submit', data: data);
    } on DioError catch (e) {
      throw Exception('提交失败: ${e.message}');
    }
  }
}

本地存储

1. SharedPreferences

dart
import 'package:shared_preferences/shared_preferences.dart';

class LocalStorage {
  static Future<void> saveString(String key, String value) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(key, value);
  }

  static Future<String> getString(String key) async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString(key);
  }

  static Future<void> remove(String key) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(key);
  }
}

// 使用示例
class SettingsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('设置'),
      ),
      body: FutureBuilder<String>(
        future: LocalStorage.getString('username'),
        builder: (context, snapshot) {
          return Column(
            children: [
              Text('用户名: ${snapshot.data ?? "未设置"}'),
              ElevatedButton(
                onPressed: () async {
                  await LocalStorage.saveString('username', '张三');
                },
                child: Text('保存用户名'),
              ),
            ],
          );
        },
      ),
    );
  }
}

2. SQLite

dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  static Database _database;

  factory DatabaseHelper() => _instance;

  DatabaseHelper._internal();

  Future<Database> get database async {
    if (_database != null) return _database;
    _database = await _initDatabase();
    return _database;
  }

  Future<Database> _initDatabase() async {
    String path = join(await getDatabasesPath(), 'my_database.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }

  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE users(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        age INTEGER
      )
    ''');
  }

  Future<int> insertUser(Map<String, dynamic> user) async {
    Database db = await database;
    return await db.insert('users', user);
  }

  Future<List<Map<String, dynamic>>> getUsers() async {
    Database db = await database;
    return await db.query('users');
  }
}

性能优化

1. 列表优化

dart
class OptimizedList extends StatelessWidget {
  final List<String> items = List.generate(1000, (index) => '项目 ${index + 1}');

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(items[index]),
        );
      },
    );
  }
}

// 使用 const 构造函数
class OptimizedWidget extends StatelessWidget {
  const OptimizedWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Text(
      '优化后的文本',
      style: TextStyle(
        fontSize: 16.0,
        color: Colors.black,
      ),
    );
  }
}

2. 图片优化

dart
import 'package:cached_network_image/cached_network_image.dart';

class OptimizedImage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: 'https://example.com/image.jpg',
      placeholder: (context, url) => CircularProgressIndicator(),
      errorWidget: (context, url, error) => Icon(Icons.error),
      fit: BoxFit.cover,
    );
  }
}

// 使用 Image.asset 预加载图片
class PreloadImages extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: precacheImage(
        AssetImage('assets/images/background.jpg'),
        context,
      ),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return Image.asset('assets/images/background.jpg');
        }
        return CircularProgressIndicator();
      },
    );
  }
}

发布部署

1. Android 发布

bash
# 生成签名密钥
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload

# 配置签名信息
# 在 android/key.properties 中添加配置
storePassword=<密码>
keyPassword=<密码>
keyAlias=upload
storeFile=<密钥库文件路径>

# 构建发布版本
flutter build apk --release

# 生成 APK 文件
# 在 build/app/outputs/flutter-apk/ 目录下

2. iOS 发布

bash
# 配置证书和描述文件
# 在 Xcode 中配置

# 构建发布版本
flutter build ios --release

# 使用 Xcode 上传到 App Store
# 打开 ios/Runner.xcworkspace
# 选择 Product > Archive

最佳实践

1. 项目结构

bash
lib/
  ├── main.dart              # 应用入口
  ├── app/                   # 应用配置
  ├── screens/              # 页面
  ├── widgets/              # 可复用组件
  ├── models/               # 数据模型
  ├── services/             # 服务
  ├── utils/                # 工具函数
  └── constants/            # 常量

2. 代码规范

yaml
# analysis_options.yaml
include: package:flutter_lints/flutter.yaml

linter:
  rules:
    - always_declare_return_types
    - avoid_empty_else
    - avoid_print
    - avoid_unused_constructor_parameters
    - await_only_futures
    - camel_case_types
    - cancel_subscriptions
    - constant_identifier_names
    - control_flow_in_finally
    - empty_catches
    - empty_constructor_bodies
    - empty_statements
    - hash_and_equals
    - implementation_imports
    - library_names
    - library_prefixes
    - non_constant_identifier_names
    - package_api_docs
    - package_names
    - package_prefixed_library_names
    - prefer_const_constructors
    - prefer_final_fields
    - prefer_is_not_empty
    - prefer_single_quotes
    - prefer_typing_uninitialized_variables
    - sort_child_properties_last
    - test_types_in_equals
    - throw_in_finally
    - unnecessary_brace_in_string_interps
    - unnecessary_getters_setters
    - unnecessary_new
    - unnecessary_null_aware_assignments
    - unnecessary_statements
    - unnecessary_this
    - unrelated_type_equality_checks
    - use_rethrow_when_possible
    - valid_regexps

总结

Flutter 提供了强大的跨平台开发能力,通过合理使用框架特性和遵循最佳实践,可以构建出高性能、可维护的移动应用。

参考资源