Flutter 教程

Flutter 教程

Flutter 是 Google 推出的开源 UI 框架,使用 Dart 语言,一套代码即可构建 Android、iOS、Web、桌面多平台应用。

本教程包含

  • 基础篇:安装、第一个应用、项目结构、Widget 概念
  • 组件篇:布局、Container、ListView、用户输入、状态管理
  • 进阶篇:网络、导航、动画、本地存储、测试、打包发布

为什么选择 Flutter

  • 高性能:自绘引擎 Skia/Impeller,不依赖系统控件
  • 热重载:修改代码秒级生效,开发效率高
  • 一套代码多平台:移动端 + Web + 桌面 + 嵌入式
  • 丰富的组件库:Material 与 Cupertino 双风格

适合人群

移动应用开发者、想低成本跨平台的团队、对 Dart 感兴趣的开发者。

Flutter 安装

Flutter 安装

安装 Flutter SDK 并配置开发环境是第一步。

系统要求

  • Windows 10+ / macOS 10.14+ / Linux 64 位
  • 磁盘空间 8GB+(含 IDE 和工具)
  • Android Studio 或 VS Code(推荐)

下载 Flutter SDK

# macOS/Linux
cd ~/development
git clone https://github.com/flutter/flutter.git -b stable

# Windows
下载官方 zip 包解压到 C:\src\flutter

配置 PATH

# macOS/Linux 编辑 ~/.zshrc
export PATH="$PATH:$HOME/development/flutter/bin"

# 生效
source ~/.zshrc

运行 flutter doctor

flutter doctor
# 检查 Android toolchain、Xcode、Chrome 等依赖

安装 IDE 插件

# VS Code:安装 Flutter 和 Dart 扩展
# Android Studio:安装 Flutter 插件

验证

flutter --version

第一个 Flutter 应用

第一个 Flutter 应用

创建并运行你的第一个 Flutter 应用,感受热重载的魔力。

创建项目

flutter create my_app
cd my_app

运行应用

flutter run
# 选择连接的设备(模拟器/真机/浏览器)

修改计数器示例

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(colorSchemeSeed: Colors.blue),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('计数器')),
      body: Center(
        child: Text('点击次数: $_counter', style: const TextStyle(fontSize: 24)),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

热重载

保存代码后按 r 键热重载(状态保留)或 R 完全重启,立即看到修改。

Flutter 项目结构

Flutter 项目结构

理解 Flutter 项目的目录结构,才能高效组织代码。

标准结构

my_app/
├── lib/                  # Dart 源码(主要工作目录)
│   └── main.dart         # 入口文件
├── test/                 # 测试代码
├── android/              # Android 原生工程
├── ios/                  # iOS 原生工程
├── web/                  # Web 支持
├── linux/ macos/ windows/# 桌面平台工程
├── pubspec.yaml          # 依赖和资源配置
├── analysis_options.yaml # 代码规范
└── README.md

pubspec.yaml 关键部分

name: my_app
description: 示例应用
version: 1.0.0+1

environment:
  sdk: '>=3.0.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0          # 第三方依赖

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  uses-material-design: true
  assets:
    - assets/images/     # 资源文件

安装依赖

flutter pub get   # 下载依赖
flutter pub add http   # 添加新依赖

Flutter Widget 基础

Flutter Widget 基础

Flutter 中一切都是 Widget(组件),UI 由 Widget 树构建而成。

核心概念

  • Widget:描述 UI 的不可变配置
  • Element:Widget 在树中的实例
  • RenderObject:负责实际绘制和布局

StatelessWidget(无状态)

class MyText extends StatelessWidget {
  const MyText({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('你好, Flutter');
  }
}

StatefulWidget(有状态)

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$count'),
        ElevatedButton(
          onPressed: () => setState(() => count++),
          child: const Text('加一'),
        ),
      ],
    );
  }
}

Widget 树结构

MaterialApp
└── Scaffold
    ├── AppBar
    ├── Body (Center → Column → Text/Button)
    └── FloatingActionButton

setState 的作用

调用 setState 后 Flutter 会重新调用 build 方法重建 UI,是状态更新的核心。

Flutter StatelessWidget

Flutter StatelessWidget

StatelessWidget 是无状态组件:一旦构建完成,自身不再变化,适合展示静态内容。

使用场景

  • 静态文本、图标、图片展示
  • 纯展示型组件(标题、标签、分隔线)
  • 数据来自外部且不会自变的组件

定义

class Greeting extends StatelessWidget {
  const Greeting({super.key, required this.name});

  final String name;   // 外部传入的参数

  @override
  Widget build(BuildContext context) {
    return Text('Hello, $name!');
  }
}

// 使用
const Greeting(name: 'Flutter')

build 方法

  • 唯一必须实现的方法
  • 返回 Widget 描述 UI
  • 不能在此方法中做耗时操作或修改状态

组合示例

class ProfileCard extends StatelessWidget {
  const ProfileCard({super.key, required this.name, required this.avatar});

  final String name;
  final String avatar;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Row(
        children: [
          CircleAvatar(backgroundImage: NetworkImage(avatar)),
          const SizedBox(width: 12),
          Text(name, style: Theme.of(context).textTheme.titleLarge),
        ],
      ),
    );
  }
}

Flutter 布局基础

Flutter 布局基础

Flutter 布局通过组合布局 Widget 实现,核心是"约束向下传递,尺寸向上回报"。

常用布局 Widget

  • Row:水平排列子组件
  • Column:垂直排列子组件
  • Stack:叠加排列(层叠)
  • Center:居中
  • Padding:内边距
  • Container:容器(装饰+约束)
  • Expanded / Flexible:弹性分配空间

Row 与 Column

Column(
  crossAxisAlignment: CrossAxisAlignment.start,  // 交叉轴对齐
  mainAxisAlignment: MainAxisAlignment.center,   // 主轴对齐
  children: [
    Text('第一行'),
    Row(
      children: [
        Icon(Icons.star, color: Colors.amber),
        const SizedBox(width: 8),
        Text('评分 4.8'),
      ],
    ),
  ],
)

Expanded 弹性布局

Row(
  children: [
    Expanded(flex: 2, child: Container(color: Colors.red)),
    Expanded(flex: 1, child: Container(color: Colors.blue)),
  ],
)
// 红色占 2/3,蓝色占 1/3

Stack 层叠

Stack(
  children: [
    Container(width: 200, height: 200, color: Colors.grey),
    Positioned(
      top: 10, right: 10,
      child: Icon(Icons.favorite, color: Colors.red),
    ),
  ],
)

Flutter Container

Flutter Container

Container 是最常用的容器 Widget,集装饰、约束、对齐于一体。

常用属性

Container(
  width: 200,
  height: 100,
  padding: const EdgeInsets.all(16),      // 内边距
  margin: const EdgeInsets.symmetric(horizontal: 20),  // 外边距
  alignment: Alignment.center,            // 子组件对齐
  decoration: BoxDecoration(              // 装饰
    color: Colors.blue,
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow(color: Colors.black26, blurRadius: 8),
    ],
  ),
  child: const Text('内容'),
)

EdgeInsets 用法

EdgeInsets.all(8)              // 四周 8
EdgeInsets.only(left: 10)      // 仅左边
EdgeInsets.symmetric(horizontal: 16, vertical: 8)  // 水平16垂直8
EdgeInsets.fromLTRB(1, 2, 3, 4) // 上右下左

BoxDecoration 常用

BoxDecoration(
  color: Colors.white,
  gradient: LinearGradient(        // 渐变
    colors: [Colors.blue, Colors.purple],
  ),
  border: Border.all(color: Colors.grey, width: 1),
  borderRadius: BorderRadius.circular(8),
  image: DecorationImage(image: NetworkImage(url)),
)

替代方案

纯约束用 SizedBox,纯间距用 Padding,避免 Container 嵌套过深影响性能。

Flutter ListView

Flutter ListView

ListView 是滚动列表组件,移动应用最常用的布局之一。

简单列表

ListView(
  children: [
    ListTile(title: Text('项目 1')),
    ListTile(title: Text('项目 2')),
    ListTile(
      leading: Icon(Icons.star),
      title: Text('带图标'),
      trailing: Icon(Icons.chevron_right),
      onTap: () { /* 点击事件 */ },
    ),
  ],
)

大量数据:ListView.builder

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
      onTap: () => print('点击 $index'),
    );
  },
)
// 惰性构建,只渲染可见项,适合长列表

分隔列表

ListView.separated(
  itemCount: 10,
  separatorBuilder: (_, __) => const Divider(),
  itemBuilder: (context, index) => ListTile(title: Text('第 $index 项')),
)

横向列表

SizedBox(
  height: 120,
  child: ListView(
    scrollDirection: Axis.horizontal,   // 横向滚动
    children: [/* ... */],
  ),
)

刷新与加载更多

RefreshIndicator(
  onRefresh: () async { await loadData(); },
  child: ListView(...),
)

Flutter 用户输入

Flutter 用户输入

TextField 是用户文本输入的核心组件,配合控制器管理输入内容。

基础文本框

TextField(
  controller: _controller,          // 控制器
  decoration: InputDecoration(
    labelText: '用户名',
    hintText: '请输入用户名',
    prefixIcon: Icon(Icons.person),
    border: OutlineInputBorder(),
  ),
)

控制器与监听

final _controller = TextEditingController();

@override
void initState() {
  super.initState();
  // 监听输入变化
  _controller.addListener(() {
    print('当前输入: ${_controller.text}');
  });
}

@override
void dispose() {
  _controller.dispose();   // 必须释放
  super.dispose();
}

密码与键盘类型

TextField(
  obscureText: true,                      // 密码模式
  keyboardType: TextInputType.emailAddress,  // 邮箱键盘
  textInputAction: TextInputAction.done,
  onSubmitted: (value) => login(value),   // 回车回调
)

表单验证(Form)

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        validator: (value) {
          if (value == null || value.isEmpty) {
            return '不能为空';
          }
          return null;
        },
      ),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            // 验证通过
          }
        },
        child: const Text('提交'),
      ),
    ],
  ),
)

Flutter 状态管理

Flutter 状态管理

状态管理决定数据如何在组件间共享和更新,是 Flutter 应用架构的核心。

状态管理方案对比

方案特点适用
setState最简单,局部状态单组件状态
InheritedWidget官方基础,自建框架跨层共享
Provider官方推荐,轻量好用中小型应用
RiverpodProvider 进阶,编译安全中大型应用
Bloc事件驱动,结构清晰大型复杂应用
GetX轻量全能,入门快快速开发

Provider 基本用法

// 1. 定义模型
class Counter extends ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

// 2. 顶层提供
runApp(
  ChangeNotifierProvider(
    create: (_) => Counter(),
    child: const MyApp(),
  ),
);

// 3. 组件消费
final counter = context.watch<Counter>();  // 监听变化
Text('${counter.count}')
context.read<Counter>().increment();      // 触发操作

选择建议

初学者从 Provider 开始,业务复杂后迁移 Riverpod 或 Bloc。

Flutter HTTP 请求

Flutter HTTP 请求

Flutter 通过 http 或 dio 包发送网络请求,与后端 API 交互。

添加依赖

flutter pub add http

GET 请求

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

Future<Map<String, dynamic>> fetchPost() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
  );

  if (response.statusCode == 200) {
    return jsonDecode(response.body) as Map<String, dynamic>;
  } else {
    throw Exception('请求失败: ${response.statusCode}');
  }
}

POST 请求

Future<void> createPost() async {
  final response = await http.post(
    Uri.parse('https://jsonplaceholder.typicode.com/posts'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'title': 'Flutter 教程',
      'body': '正文内容',
    }),
  );
}

FutureBuilder 加载数据

FutureBuilder<Map>(
  future: fetchPost(),
  builder: (context, snapshot) {
    if (snapshot.connectionState != ConnectionState.done) {
      return const CircularProgressIndicator();  // 加载中
    }
    if (snapshot.hasError) {
      return Text('错误: ${snapshot.error}');     // 错误
    }
    return Text(snapshot.data!['title']);         // 成功
  },
)

dio 进阶

flutter pub add dio

final dio = Dio(BaseOptions(
  baseUrl: 'https://api.example.com',
  connectTimeout: Duration(seconds: 10),
));
// 拦截器、上传下载进度等高级功能

Flutter 导航与路由

Flutter 导航与路由

路由(Route)管理页面跳转,Flutter 提供 Navigator 和命名路由。

基本跳转

// 跳转到新页面
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => const DetailPage(id: 1)),
);

// 返回上一页
Navigator.pop(context);

// 返回并传值
Navigator.pop(context, '结果数据');

接收返回值

final result = await Navigator.push<String>(
  context,
  MaterialPageRoute(builder: (context) => const ChoosePage()),
);
print('选择结果: $result');

命名路由

// 在 MaterialApp 中定义
MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomePage(),
    '/detail': (context) => const DetailPage(),
  },
);

// 跳转
Navigator.pushNamed(context, '/detail', arguments: 42);

// 接收参数
final args = ModalRoute.of(context)!.settings.arguments;

页面替换与清栈

Navigator.pushReplacement(...);   // 替换当前页
Navigator.pushAndRemoveUntil(...); // 跳到新页并移除旧页(登录后常见)

go_router(推荐)

flutter pub add go_router
// 声明式路由,支持深链接、嵌套导航

Flutter 表单与验证

Flutter 表单与验证

Form + TextFormField 提供完整的表单验证能力,是登录注册页面的核心。

完整示例

class LoginForm extends StatefulWidget {
  const LoginForm({super.key});
  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();
  final _email = TextEditingController();
  final _password = TextEditingController();
  bool _loading = false;

  Future<void> _submit() async {
    if (_formKey.currentState!.validate()) {
      setState(() => _loading = true);
      // 调用登录 API
      await login(_email.text, _password.text);
      setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            controller: _email,
            decoration: const InputDecoration(labelText: '邮箱'),
            keyboardType: TextInputType.emailAddress,
            validator: (v) =>
                !(v != null && v.contains('@')) ? '邮箱格式错误' : null,
          ),
          TextFormField(
            controller: _password,
            obscureText: true,
            decoration: const InputDecoration(labelText: '密码'),
            validator: (v) =>
                (v == null || v.length < 6) ? '密码至少6位' : null,
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: _loading ? null : _submit,
            child: _loading
                ? const CircularProgressIndicator()
                : const Text('登录'),
          ),
        ],
      ),
    );
  }
}

常用验证器

// 非空、邮箱、长度、自定义正则
validator: (v) => v!.isEmpty ? '必填' : null

表单重置

_formKey.currentState!.reset();

Flutter 主题与样式

Flutter 主题与样式

通过 ThemeData 统一管理颜色、字体、组件样式,实现全局一致的设计。

定义主题

MaterialApp(
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
    useMaterial3: true,               // Material 3 风格
    appBarTheme: const AppBarTheme(
      centerTitle: true,
      backgroundColor: Colors.teal,
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
      ),
    ),
  ),
  darkTheme: ThemeData.dark(),        // 深色主题
  themeMode: ThemeMode.system,        // 跟随系统
)

使用主题

// 获取主题样式
Theme.of(context).colorScheme.primary
Theme.of(context).textTheme.titleLarge
Theme.of(context).textTheme.bodyMedium

Text(
  '标题',
  style: Theme.of(context).textTheme.headlineSmall,
)

局部主题覆盖

Text(
  '特殊样式',
  style: TextStyle(
    color: Colors.red,
    fontWeight: FontWeight.bold,
    fontSize: 20,
  ),
)

深色模式适配

final isDark = Theme.of(context).brightness == Brightness.dark;
// 根据 isDark 选择不同颜色/资源

Flutter 动画基础

Flutter 动画基础

Flutter 动画分为隐式动画(简单)和显式动画(复杂可控)。

隐式动画

class FadeDemo extends StatefulWidget {
  const FadeDemo({super.key});
  @override
  State<FadeDemo> createState() => _FadeDemoState();
}

class _FadeDemoState extends State<FadeDemo> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        AnimatedOpacity(          // 透明度动画
          opacity: _visible ? 1.0 : 0.0,
          duration: const Duration(milliseconds: 500),
          child: const Text('淡入淡出'),
        ),
        AnimatedContainer(        // 容器动画
          duration: const Duration(seconds: 1),
          width: _visible ? 200 : 100,
          height: _visible ? 200 : 100,
          color: _visible ? Colors.blue : Colors.red,
        ),
        ElevatedButton(
          onPressed: () => setState(() => _visible = !_visible),
          child: const Text('切换'),
        ),
      ],
    );
  }
}

显式动画(AnimationController)

class FadeIn extends StatefulWidget {
  const FadeIn({super.key});
  @override
  State<FadeIn> createState() => _FadeInState();
}

class _FadeInState extends State<FadeIn>
    with SingleTickerProviderStateMixin {
  late final AnimationController _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 2),
  );

  @override
  void initState() {
    super.initState();
    _controller.forward();
  }

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

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _controller,
      child: const Text('入场动画'),
    );
  }
}

常用动画组件

AnimatedOpacity / AnimatedContainer / AnimatedSwitcher
Hero(页面转场) / TweenAnimationBuilder / Lottie

Flutter 本地存储

Flutter 本地存储

Flutter 常用 shared_preferences(键值)和 sqflite(SQLite 数据库)做本地存储。

shared_preferences(简单键值)

flutter pub add shared_preferences

// 写入
final prefs = await SharedPreferences.getInstance();
await prefs.setString('username', 'laowang');
await prefs.setInt('score', 100);
await prefs.setBool('isLogin', true);

// 读取
final name = prefs.getString('username');
final score = prefs.getInt('score') ?? 0;

// 删除
await prefs.remove('username');

sqflite(SQLite 数据库)

flutter pub add sqflite path

// 打开数据库
final db = await openDatabase(
  'my_app.db',
  version: 1,
  onCreate: (db, version) async {
    await db.execute("""
      CREATE TABLE users(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        age INTEGER
      )
    ");
  },
);

// 增删改查
await db.insert('users', {'name': 'Tom', 'age': 20});
final users = await db.query('users');
await db.update('users', {'age': 21}, where: 'name = ?', whereArgs: ['Tom']);
await db.delete('users', where: 'name = ?', whereArgs: ['Tom']);

其他方案

hive        高性能 NoSQL 本地库
isar        Hive 作者新作,性能更强
path_provider 获取应用目录路径

Flutter 图片与资源

Flutter 图片与资源

Flutter 支持本地资源图片、网络图片和内存图片等多种来源。

本地资源图片

# pubspec.yaml 声明资源
flutter:
  assets:
    - assets/images/logo.png
    - assets/images/   # 整个目录

// 使用
Image.asset('assets/images/logo.png')

网络图片

Image.network(
  'https://example.com/image.jpg',
  loadingBuilder: (context, child, progress) {
    return progress == null
        ? child
        : const CircularProgressIndicator();
  },
  errorBuilder: (context, error, stack) {
    return const Icon(Icons.broken_image);   // 加载失败占位
  },
)

缓存优化(cached_network_image)

flutter pub add cached_network_image

CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
)

图片填充模式

Image.asset(
  'assets/images/bg.png',
  fit: BoxFit.cover,      // 覆盖填满(裁剪)
  fit: BoxFit.contain,    // 完整显示(留白)
  fit: BoxFit.fill,       // 拉伸填满(变形)
)

圆形头像

CircleAvatar(
  radius: 40,
  backgroundImage: NetworkImage(url),
)

Flutter DevTools

Flutter DevTools

DevTools 是 Flutter 的性能调试工具套件,浏览器中运行,功能强大。

启动 DevTools

# 应用运行时,VS Code 按 F5 调试,或:
flutter run   # 然后按 w 打开 DevTools

核心功能

  • Widget Inspector:可视化查看 Widget 树,检查布局
  • Performance:性能分析,帧率、耗时
  • Memory:内存使用监控,发现泄漏
  • Network:网络请求查看(需 Dart VM 服务)
  • Debugger:断点调试
  • Logging:日志查看

Widget Inspector 技巧

# 选择模式:点击界面元素查看对应 Widget
# 检查布局约束,快速定位溢出问题

性能分析

# 打开 Performance 页,录制并重放
# 关注:build 耗时、Raster 线程
# 常见问题:重建过多、图片过大

命令行模式

dart devtools

日志与调试输出

debugPrint('调试信息');   // 比 print 更友好
FlutterError.onError      // 全局错误处理

Flutter 平台特定代码

Flutter 平台特定代码

通过平台通道(Platform Channel),Flutter 可以调用 Android/iOS 原生功能。

MethodChannel 原理

Dart 代码 ⇄ MethodChannel ⇄ 原生代码(Java/Kotlin/Swift)

Dart 侧调用

import 'package:flutter/services.dart';

static const platform = MethodChannel('com.example/app');

Future<String> getBatteryLevel() async {
  try {
    final result = await platform.invokeMethod('getBatteryLevel');
    return result as String;
  } on PlatformException catch (e) {
    return '获取失败: ${e.message}';
  }
}

Android 原生侧(Kotlin)

class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger,
            "com.example/app").setMethodCallHandler { call, result ->
            if (call.method == "getBatteryLevel") {
                val battery = getBattery()
                if (battery != null) result.success(battery)
                else result.error("UNAVAILABLE", "无法获取", null)
            } else {
                result.notImplemented()
            }
        }
    }
}

iOS 原生侧(Swift)

override func viewDidLoad() {
    super.viewDidLoad()
    let channel = FlutterMethodChannel(
        name: "com.example/app",
        binaryMessenger: binaryMessenger)
    channel.setMethodCallHandler { call, result in
        if call.method == "getBatteryLevel" {
            result.success(getBatteryLevel())
        } else {
            result.notImplemented()
        }
    }
}

常用原生插件

url_launcher   打开网页/电话
permission_handler  权限管理
camera / image_picker 相机相册
flutter_local_notifications 本地通知
geolocator      定位

Flutter 测试

Flutter 测试

Flutter 支持单元测试、Widget 测试和集成测试三层测试体系。

单元测试

// test/calculator_test.dart
import 'package:flutter_test/flutter_test.dart';

int add(int a, int b) => a + b;

void main() {
  test('加法测试', () {
    expect(add(1, 2), 3);
    expect(add(-1, 1), 0);
  });

  group('计算器', () {
    test('加法', () => expect(add(2, 3), 5));
    test('减法', () => expect(add(5, -2), 3));
  });
}

Widget 测试

// test/widget_test.dart
testWidgets('计数器显示正确', (tester) async {
  await tester.pumpWidget(const MyApp());

  expect(find.text('0'), findsOneWidget);

  await tester.tap(find.byIcon(Icons.add));
  await tester.pump();

  expect(find.text('1'), findsOneWidget);
});

运行测试

flutter test            # 全部测试
flutter test test/widget_test.dart  # 指定文件
flutter test --coverage # 覆盖率

集成测试(integration_test)

flutter test integration_test -d emulator-5554

测试最佳实践

  • 纯逻辑(工具函数、模型)写单元测试
  • 页面交互写 Widget 测试
  • 关键流程(登录、下单)写集成测试
  • 保持测试独立,避免依赖顺序

Flutter 打包与发布

Flutter 打包与发布

将 Flutter 应用打包成 APK/IPA 并发布到应用商店。

Android 打包

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

# 配置 key.properties(android/key.properties)
storePassword=xxx
keyPassword=xxx
keyAlias=upload
storeFile=../upload.jks

# 构建 APK
flutter build apk              # debug 验证
flutter build apk --release    # 正式包
flutter build appbundle        # AAB(Google Play 推荐)

iOS 打包

flutter build ios --release
# 用 Xcode 打开 ios/ 目录,配置签名
# Product → Archive → 上传 App Store

版本号管理

# pubspec.yaml
version: 1.2.3+45   # 1.2.3 版本号, 45 构建号

应用图标与名称

# Android: android/app/src/main/res/ 下的 mipmap 目录
# iOS: ios/Runner/Assets.xcassets/AppIcon.appiconset
# 可用 flutter_launcher_icons 一键生成
flutter pub add --dev flutter_launcher_icons

发布前检查清单

  • 移除 debug 打印
  • 检查权限是否合理
  • 混淆与加固(Android)
  • 隐私政策
  • 真机全流程回归测试

Flutter 最佳实践

Flutter 最佳实践

总结 Flutter 开发中的架构与工程最佳实践,让项目健壮可维护。

项目架构

lib/
├── main.dart              # 入口
├── models/                # 数据模型
├── services/              # API/网络层
├── providers/             # 状态管理
├── pages/                 # 页面
├── widgets/               # 通用组件
├── utils/                 # 工具函数
└── theme/                 # 主题配置

代码规范

flutter analyze            # 静态分析
flutter format .           # 自动格式化
# 遵循 Effective Dart 风格指南

性能优化

  • 长列表用 ListView.builder(惰性构建)
  • 避免不必要的 setState 重建
  • const 构造函数减少重建
  • 图片加缓存与压缩(cached_network_image)
  • 避免在 build 中做耗时操作

错误处理

try {
  final data = await api.fetch();
} on TimeoutException {
  // 超时处理
} on ApiException catch (e) {
  // 业务错误
} catch (e) {
  // 兜底
}

安全

  • 密钥放后端或安全存储(flutter_secure_storage)
  • 敏感信息不进 Git
  • 网络请求校验证书

版本管理

git init
git add .
git commit -m "初始提交"
# 配合 CI:GitHub Actions 自动构建测试

Firebase 集成

Firebase 集成

Firebase 是 Google 的 BaaS 平台,提供认证、数据库、推送、分析等能力,与 Flutter 集成方便。

开通与配置

# 1. 在 Firebase 控制台创建项目
# 2. 添加 Android/iOS 应用并下载配置文件
# 3. 安装 FlutterFire CLI
dart pub global activate flutterfire_cli
flutterfire configure

常用 Firebase 服务

flutter pub add firebase_core
flutter pub add firebase_auth          # 认证
flutter pub add cloud_firestore        # NoSQL 数据库
flutter pub add firebase_storage       # 文件存储
flutter pub add firebase_messaging     # 推送通知
flutter pub add firebase_analytics     # 分析

初始化

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

邮箱密码认证

final auth = FirebaseAuth.instance;

// 注册
await auth.createUserWithEmailAndPassword(
  email: email,
  password: password,
);

// 登录
await auth.signInWithEmailAndPassword(
  email: email,
  password: password,
);

// 监听登录状态
auth.authStateChanges().listen((user) {
  // user != null 表示已登录
});

Firestore 读写

// 写入
await FirebaseFirestore.instance
    .collection('users')
    .doc('laowang')
    .set({'name': '老王', 'age': 30});

// 读取(实时)
FirebaseFirestore.instance
    .collection('posts')
    .snapshots()
    .listen((snapshot) {
  // 数据变化自动更新
});

注意

国内网络访问 Firebase 可能不稳定,生产环境需评估合规与替代方案(自建后端)。

Flutter 进阶

Flutter 进阶

本教程最后一章,介绍 Flutter 进阶方向与成长路径。

进阶主题

  • 渲染原理:深入 Widget/Element/RenderObject 三层
  • 自定义绘制:CustomPainter 绘制图形、图表
  • 手势系统:GestureDetector、缩放、拖拽
  • 性能剖析:Profile 模式、帧耗时分析
  • 隔离区(Isolate):多线程处理耗时任务
  • 国际化(i18n):多语言支持
  • 无障碍:语义、屏幕阅读器支持

Isolate 并发示例

import 'dart:isolate';

// 耗时计算放到后台隔离区
final result = await compute(heavyTask, data);

int heavyTask(int n) {
  var sum = 0;
  for (var i = 0; i < n; i++) {
    sum += i;
  }
  return sum;
}

自定义绘制

class CirclePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;
    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      size.width / 2,
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

// 使用
CustomPaint(size: const Size(100, 100), painter: CirclePainter())

学习路线

  1. 掌握 Widget 体系与状态管理(Provider)
  2. 实战 2-3 个完整 App
  3. 深入渲染与性能优化
  4. 学习 Riverpod/Bloc 大型架构
  5. 关注 Flutter 新版本特性(Impeller、Wasm)

社区资源

  • 官方文档:docs.flutter.dev
  • 中文社区:Flutter 中文开发者社区
  • 开源项目:GitHub 搜 awesome-flutter
第 1 / 25 节