这是 《Flutter 全组件深度解析手册》的第五部分(完结篇)

在前面的四个章节中,我们已经构建了 App 的“骨架”(Scaffold)、“肌肉”(Layout)、“皮肤”(Style)和“神经系统”(Navigation)。

第五部分将赋予 App “灵魂”与“感知能力”。
我们将探讨:

  1. 手势交互 (Gestures):除了点击按钮,如何响应拖拽、长按、缩放等复杂操作?
  2. 异步 UI 构建 (Async UI):这是 Flutter 最强大的特性之一。如何优雅地处理“正在加载中”、“加载失败”、“数据渲染”这三种状态?
  3. 主题与拦截 (Theme & PopScope):全局样式管理与安卓物理返回键的拦截。

第五部分:功能性组件与异步编程 (Functional & Async)

第八章:功能性组件 (Functional Widgets)

这类组件通常不可见(没有 UI 绘制),但它们包裹在其他组件外层,为其提供特定的功能。

1. GestureDetector (手势检测器)

说明:
GestureDetector 是一个“隐形”的万能组件。它可以包裹任意 Widget,使其具备响应手势的能力(点击、双击、长按、拖拽、缩放)。

核心属性 (Callbacks):

  • onTap: 单击。
  • onDoubleTap: 双击。
  • onLongPress: 长按。
  • onPanUpdate: 拖拽/平移(获取坐标移动)。
  • onScaleUpdate: 缩放。

代码示例:

import 'package:flutter/material.dart';

class GestureExample extends StatefulWidget {
  
  _GestureExampleState createState() => _GestureExampleState();
}

class _GestureExampleState extends State<GestureExample> {
  String _action = "请尝试:点击、双击或长按";
  Color _color = Colors.blue;

  
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        // 1. 单击
        onTap: () {
          setState(() {
            _action = "检测到:单击";
            _color = Colors.blue;
          });
        },
        // 2. 双击
        onDoubleTap: () {
          setState(() {
            _action = "检测到:双击";
            _color = Colors.green;
          });
        },
        // 3. 长按
        onLongPress: () {
          setState(() {
            _action = "检测到:长按";
            _color = Colors.red;
          });
        },
        // 被包裹的组件
        child: Container(
          width: 200,
          height: 200,
          color: _color,
          alignment: Alignment.center,
          child: Text(_action, style: TextStyle(color: Colors.white)),
        ),
      ),
    );
  }
}

2. InkWell (水波纹触摸)

说明:
如果你想要 Material Design 风格的点击效果(点击时有水波纹扩散),请使用 InkWell 代替 GestureDetector

注意: InkWell 必须有一个 Material 风格的祖先(如 Scaffold, Card, 或直接包裹 Material 组件),否则水波纹画不出来。

代码示例:

Material(
  color: Colors.transparent, // 必须透明,否则会遮挡下面的颜色
  child: InkWell(
    splashColor: Colors.blue.withOpacity(0.3), // 水波纹颜色
    borderRadius: BorderRadius.circular(10), // 点击区域圆角
    onTap: () {
      print("InkWell Tapped");
    },
    child: Container(
      padding: EdgeInsets.all(20),
      child: Text("点击我看水波纹特效"),
    ),
  ),
)

3. Theme (主题管理)

说明:
Flutter 通过 ThemeData 管理全局样式。你可以在 MaterialApp 中定义好颜色和字体,然后在任何子组件中通过 Theme.of(context) 获取这些样式。

应用场景: 一键切换“夜间模式”或“品牌色”。

代码示例:

// 1. 在 MaterialApp 中定义
MaterialApp(
  theme: ThemeData(
    primaryColor: Colors.purple, // 主色调
    scaffoldBackgroundColor: Colors.white,
    // 定义通用的文本样式
    textTheme: TextTheme(
      headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
      bodyMedium: TextStyle(fontSize: 14, color: Colors.grey),
    ),
  ),
  home: ThemePage(),
);

// 2. 在子页面中使用
class ThemePage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    // 获取当前的主题配置
    final theme = Theme.of(context);
    
    return Scaffold(
      appBar: AppBar(title: Text("Theme Demo")),
      body: Center(
        child: Column(
          children: [
            // 使用主题定义的颜色和样式
            Text("Headline", style: theme.textTheme.headlineLarge),
            Text("Body Text", style: theme.textTheme.bodyMedium),
            Icon(Icons.star, color: theme.primaryColor),
          ],
        ),
      ),
    );
  }
}

4. PopScope (拦截返回键)

说明:
在 Android 上,用户可能会误触物理返回键退出应用。Flutter 3.12+ 引入了 PopScope(替代了旧的 WillPopScope)来拦截这一行为。

代码示例(防误触退出):

class ExitConfirmPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return PopScope(
      canPop: false, // 禁止直接返回
      onPopInvoked: (didPop) async {
        if (didPop) return;

        // 弹出确认框
        final shouldPop = await showDialog<bool>(
          context: context,
          builder: (context) {
            return AlertDialog(
              title: Text("提示"),
              content: Text("确定要退出吗?"),
              actions: [
                TextButton(
                  onPressed: () => Navigator.pop(context, false),
                  child: Text("取消"),
                ),
                TextButton(
                  onPressed: () => Navigator.pop(context, true),
                  child: Text("退出"),
                ),
              ],
            );
          },
        );

        // 如果用户点击了退出,手动执行 pop
        if (shouldPop == true) {
          if (context.mounted) Navigator.pop(context);
        }
      },
      child: Scaffold(
        appBar: AppBar(title: Text("拦截返回示例")),
        body: Center(child: Text("按安卓返回键试试")),
      ),
    );
  }
}

第九章:异步 UI 构建 (Asynchronous UI)

这是 Flutter 开发中最重要的章节之一。几乎所有的真实 App 都需要从网络获取数据。数据获取是异步的(Future),在数据回来之前,页面应该显示什么?

传统做法需要手动定义 isLoading 变量并使用 setState 切换。Flutter 提供了更优雅的方案:FutureBuilder

1. FutureBuilder (异步构建器)

说明:
它根据 Future 的状态(未开始、进行中、已完成、报错)自动构建不同的 UI。

核心属性:

  • future: 需要等待的异步任务(通常是一个网络请求函数)。
  • builder: 回调函数 (context, snapshot)snapshot 包含了当前的状态和数据。

常见快照状态 (snapshot.connectionState):

  • waiting: 正在加载。
  • done: 加载完成(成功或失败)。

代码示例 (模拟网络请求):

import 'package:flutter/material.dart';

// 1. 模拟一个耗时的网络请求函数
Future<String> fetchUserData() async {
  await Future.delayed(Duration(seconds: 3)); // 模拟延迟3秒
  // 模拟随机成功或失败
  if (DateTime.now().second % 2 == 0) {
    throw Exception("网络连接失败");
  }
  return "用户数据:张三 (ID: 1001)";
}

class FutureBuilderExample extends StatefulWidget {
  
  _FutureBuilderExampleState createState() => _FutureBuilderExampleState();
}

class _FutureBuilderExampleState extends State<FutureBuilderExample> {
  // ⚠️ 重要:Future 必须在 initState 中初始化,
  // 如果直接写在 build 方法里:FutureBuilder(future: fetchUserData()...
  // 那么每次父组件刷新,都会导致请求重新发送!
  late Future<String> _dataFuture;

  
  void initState() {
    super.initState();
    _dataFuture = fetchUserData();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("异步 UI 加载")),
      body: Center(
        child: FutureBuilder<String>(
          future: _dataFuture,
          builder: (context, snapshot) {
            // 1. 判断状态:是否正在连接
            if (snapshot.connectionState == ConnectionState.waiting) {
              return CircularProgressIndicator(); // 显示转圈圈
            }

            // 2. 判断状态:是否有错误
            if (snapshot.hasError) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.error, color: Colors.red, size: 50),
                  Text("错误: ${snapshot.error}"),
                  ElevatedButton(
                    onPressed: () {
                      setState(() {
                        _dataFuture = fetchUserData(); // 点击重试
                      });
                    },
                    child: Text("重试"),
                  )
                ],
              );
            }

            // 3. 显示数据
            if (snapshot.hasData) {
              return Text(
                snapshot.data!,
                style: TextStyle(fontSize: 20, color: Colors.green),
              );
            }

            return Text("无数据");
          },
        ),
      ),
    );
  }
}

2. StreamBuilder (流构建器)

说明:
FutureBuilder 类似,但 Future 只返回一次数据,而 Stream 是一系列源源不断的数据(如:WebSocket 聊天消息、下载进度、倒计时)。StreamBuilder 会在每次收到新数据时重建 UI。

代码示例 (倒计时):

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

class StreamBuilderExample extends StatelessWidget {
  // 创建一个每秒发出一个数字的流
  Stream<int> _timerStream() {
    return Stream.periodic(Duration(seconds: 1), (count) => 10 - count).take(11);
  }

  
  Widget build(BuildContext context) {
    return Center(
      child: StreamBuilder<int>(
        stream: _timerStream(),
        initialData: 10, // 初始值
        builder: (context, snapshot) {
          if (snapshot.data == 0) {
            return Text("倒计时结束!", style: TextStyle(fontSize: 30, color: Colors.red));
          }
          return Text(
            "倒计时: ${snapshot.data}",
            style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
          );
        },
      ),
    );
  }
}

总结与完结 (Conclusion)

恭喜你!通过这五部分的学习,你已经完成了 《Flutter 全组件深度解析手册》 的核心课程。

让我们回顾一下你的武器库:

  1. 基础 (Part 1): 使用 Scaffold, Text, Row, Column, Container 搭建静态页面。
  2. 布局 (Part 2): 使用 ListView, GridView, Stack 处理复杂排版和滚动列表。
  3. 交互 (Part 3): 使用 TextField, Form, Dialog 获取用户输入并给予反馈。
  4. 导航 (Part 4): 使用 Navigator, BottomNavigationBar 构建多页面应用架构。
  5. 逻辑 (Part 5): 使用 GestureDetector, FutureBuilder, Theme 处理手势、异步网络数据和全局样式。
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐