Flutter零基础速成指南

Flutter 从入门到深入:小白专属教学计划

核心目标

从「零编程基础」或「非Flutter开发背景」出发,逐步掌握 Flutter 开发全流程:

  1. 夯实 Dart 语法与 Flutter 基础组件;
  2. 熟练运用实战核心技能(状态管理、网络请求、本地存储等);
  3. 能独立开发完整 App 并打包发布;
  4. 理解 Flutter 底层原理,具备问题排查与性能优化能力。

整体学习节奏(12周,可按需调整)

阶段 时长 核心内容 目标产出
入门启蒙 1周 环境搭建 + Dart 基础语法 + Flutter 核心概念 运行第一个 Flutter 项目
基础夯实 3周 Widget 体系 + 布局技巧 + 交互事件 + 基础页面开发 完成 2-3 个静态页面(登录、列表)
核心进阶 4周 状态管理 + 路由导航 + 网络请求 + 本地存储 + 常用插件 完成带数据交互的「简易新闻App」
项目实战 2周 完整项目开发(需求分析→架构设计→功能实现→调试优化) 独立开发「TodoList/商城Demo」
底层原理 1周 Flutter 渲染流程 + Widget/Element/RenderObject 三棵树 + 热重载原理 能解释核心原理并排查基础问题
综合提升 1周 性能优化 + 跨平台交互 + 打包发布 + 进阶特性(Flutter 3.x+ 新功能) 项目上线(应用商店/测试包)

第一阶段:入门启蒙(第1周)------ 打通「从0到1」的第一步

核心任务:搭建环境 + 理解基础逻辑

Day1:环境搭建(最关键的第一步,避免踩坑)

目标:让电脑能正常运行 Flutter 项目,搞定「环境变量 + 编辑器 + 模拟器/真机」

详细步骤:
  1. 准备工具 :
    • 编辑器:推荐 VS Code(轻量,小白友好),安装「Flutter」「Dart」插件;
    • 环境依赖:
  2. 配置环境变量 :
    • 核心:将 Flutter 的 bin 目录路径添加到系统环境变量(比如 Windows 是 此电脑→属性→高级系统设置→环境变量→Path);
    • 验证:打开终端(Windows CMD/PowerShell,Mac/Linux 终端),输入 flutter --version,能显示版本则成功。
  3. 解决依赖问题 :
    运行 flutter doctor,终端会提示缺失的依赖(比如 Android Studio、模拟器、Xcode 命令行工具等),按提示逐一安装:
    • Android 开发:安装 Android Studio,配置 SDK(推荐 API 33+),创建模拟器(比如 Pixel 5,Android 13);
    • iOS 开发(仅 Mac):打开 Xcode,安装「Command Line Tools」(Xcode→Preferences→Locations),创建 iOS 模拟器(Xcode→Open Developer Tool→Simulator)。
  4. 测试环境 :
    终端输入 flutter create my_first_app(创建项目),进入项目目录 cd my_first_app,运行 flutter run,等待模拟器/真机启动项目,看到默认的「Flutter Demo」页面则成功!
Day2-3:Dart 语法基础(Flutter 的「编程语言」)

目标:掌握 Dart 核心语法,能看懂简单代码,不用死记硬背,理解「怎么用」即可

核心知识点 + 小白解释 + 代码示例:
  1. 变量与数据类型

    • 小白解释:变量就是「存放数据的盒子」,数据类型决定「盒子里能放什么」;

    • 关键语法:

      dart 复制代码
      // 1. 用 var 声明变量(自动推断类型)
      var name = "Flutter小白"; // 字符串类型
      var age = 20; // 整数类型
      var isStudent = true; // 布尔类型
      
      // 2. 用具体类型声明(推荐,更清晰)
      String address = "中国";
      int score = 90;
      double height = 1.75;
      
      // 3. 不可变变量(final/const,数据不能改)
      final birthDate = "2000-01-01"; // 运行时确定值
      const pi = 3.14159; // 编译时确定值(常量)
  2. 函数(一段可重复执行的代码)

    • 小白解释:比如「计算两个数的和」,写一次函数,以后直接调用,不用重复写代码;

    • 关键语法:

      dart 复制代码
      // 无返回值函数(void 可省略)
      void printHello() {
        print("Hello Flutter!"); // 打印内容到控制台
      }
      
      // 有返回值函数(返回 int 类型)
      int add(int a, int b) {
        return a + b; // 返回计算结果
      }
      
      // 箭头函数(简化单行函数)
      int multiply(int a, int b) => a * b;
      
      // 调用函数
      void main() { // Dart 程序入口(必须有)
        printHello(); // 输出:Hello Flutter!
        var sum = add(2, 3);
        print(sum); // 输出:5
        var product = multiply(4, 5);
        print(product); // 输出:20
      }
  3. 条件判断与循环

    • 小白解释:让程序「做选择」(条件判断)或「重复做事」(循环);

    • 关键语法:

      dart 复制代码
      void main() {
        // 1. 条件判断(if-else)
        var score = 85;
        if (score >= 90) {
          print("优秀");
        } else if (score >= 60) {
          print("及格");
        } else {
          print("不及格");
        }
      
        // 2. 循环(for)
        for (var i = 0; i < 3; i++) {
          print("循环第 $i 次"); // 输出 3 行内容
        }
      
        // 3. 遍历列表(for-in)
        var fruits = ["苹果", "香蕉", "橙子"];
        for (var fruit in fruits) {
          print("水果:$fruit"); // 输出每个水果
        }
      }
  4. 面向对象(类与对象)

    • 小白解释:把「事物」抽象成「类」(模板),再用模板创建「对象」(具体实例),比如「人」是类,「张三」是对象;

    • 关键语法:

      dart 复制代码
      // 定义类(模板)
      class Person {
        // 属性(事物的特征)
        String name;
        int age;
      
        // 构造函数(创建对象时调用)
        Person(this.name, this.age); // 简化写法,自动赋值
      
        // 方法(事物的行为)
        void introduce() {
          print("我叫 $name,今年 $age 岁");
        }
      }
      
      void main() {
        // 创建对象(用类实例化)
        var person = Person("张三", 25);
        person.introduce(); // 调用方法,输出:我叫张三,今年25岁
      }
Day4-5:Flutter 核心概念(理解「Widget 思想」)

目标:知道 Flutter 是「用组件拼界面」,理解「Widget 是什么」「State 是什么」

核心知识点:
  1. Flutter 是什么?

    • 小白解释:一个「跨平台 App 开发框架」,写一套代码,能同时运行在 iOS 和 Android 上,还能做 Web/桌面端;
    • 核心优势:性能接近原生(因为直接编译成机器码,不是网页套壳)、UI 一致性强、开发效率高(热重载)。
  2. Widget 核心思想

    • 小白解释:Flutter 里「万物皆组件」,界面上的所有元素(文本、图片、按钮、布局)都是 Widget;
    • 关键特点:
      • 不可变(Immutable):Widget 一旦创建,属性不能改;要更新 UI,必须重新创建 Widget;
      • 组合优于继承:通过「嵌套 Widget」实现复杂 UI,而不是继承现有 Widget。
  3. 第一个 Flutter 项目解析

    打开之前创建的 my_first_app,看 lib/main.dart(核心文件):

    dart 复制代码
    import 'package:flutter/material.dart'; // 导入 Flutter  Material 组件库(官方UI库)
    
    void main() {
      runApp(const MyApp()); // 启动 App,根组件是 MyApp
    }
    
    // 无状态组件(StatelessWidget):UI 不随数据变化
    class MyApp extends StatelessWidget {
      const MyApp({super.key}); // 构造函数,key 用于组件标识
    
      // 必须实现的方法:返回一个 Widget(构建UI)
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(primarySwatch: Colors.blue), // App 主题(蓝色)
          home: const MyHomePage(title: 'Flutter 首页'), // 首页组件
        );
      }
    }
    
    // 有状态组件(StatefulWidget):UI 随数据变化(需要维护状态)
    class MyHomePage extends StatefulWidget {
      const MyHomePage({super.key, required this.title});
    
      final String title; // 接收外部传入的参数
    
      // 创建状态对象(State 与 Widget 分离,Widget 不可变,State 可变)
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    // 状态类(维护 MyHomePage 的状态)
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0; // 可变状态(点击按钮会自增)
    
      // 点击事件处理函数
      void _incrementCounter() {
        setState(() { // 关键:调用 setState 通知 Flutter 状态变化,重新构建 UI
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold( // 页面脚手架(提供 AppBar、正文区域、底部导航等)
          appBar: AppBar( // 顶部导航栏
            title: Text(widget.title), // widget 指向 MyHomePage,获取传入的 title
          ),
          body: Center( // 居中布局
            child: Column( // 垂直布局(从上到下排列子组件)
              mainAxisAlignment: MainAxisAlignment.center, // 垂直居中
              children: <Widget>[
                const Text('你点击按钮的次数:'),
                Text(
                  '$_counter', // 显示状态变量 _counter
                  style: Theme.of(context).textTheme.headlineMedium, // 文本样式
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton( // 悬浮按钮
            onPressed: _incrementCounter, // 绑定点击事件
            tooltip: 'Increment',
            child: const Icon(Icons.add), // 按钮图标
          ),
        );
      }
    }
Day6-7:练习与巩固
  1. 修改 my_first_app:
    • 把标题改成自己的名字;
    • 把悬浮按钮的图标换成 Icons.star(星星);
    • 把文本颜色改成红色。
  2. 新建一个 Dart 文件 lib/hello.dart,写一个函数,接收一个名字,返回「Hello, 名字!」,在 main.dart 中调用并打印。
  3. 遇到问题怎么办?
    • 优先看终端报错信息(红色文字),按提示排查;
    • 查 Flutter 官方文档:Flutter Docs(可切换中文);
    • 搜 Stack Overflow 或 掘金/CSDN 相关问题。

第二阶段:基础夯实(第2-4周)------ 掌握「搭界面 + 做交互」的核心能力

第2周:Widget 体系与基础布局(搭出漂亮界面)

目标:掌握常用基础 Widget 和布局技巧,能独立搭建静态页面

核心知识点 + 代码示例:
  1. 基础组件(UI 最小单元)

    组件 作用 关键属性 示例代码
    Text 显示文本 style(样式)、textAlign(对齐) Text("你好Flutter", style: TextStyle(fontSize: 20, color: Colors.red))
    Image 显示图片 asset(本地图片)、network(网络) Image.network("https://xxx.png", width: 100, height: 100)
    Icon 显示图标(Material) size、color Icon(Icons.home, size: 30, color: Colors.blue)
    RaisedButton 按钮(已过时,用 ElevatedButton) onPressed(点击事件)、child(子组件) ElevatedButton(onPressed: () {}, child: Text("点击我"))
    TextField 输入框 hintText(提示)、onChanged(输入变化) TextField(hintText: "请输入用户名")
  2. 布局组件(组织多个组件的排列)

    核心:Flutter 布局是「嵌套式」,用父组件控制子组件的排列方式

    • 线性布局(Row/Column):水平/垂直排列

      dart 复制代码
      // 水平布局(Row)
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween, // 子组件两端对齐
        children: [
          Text("左侧文本"),
          Icon(Icons.arrow_right),
          Text("右侧文本"),
        ],
      );
      
      // 垂直布局(Column)
      Column(
        crossAxisAlignment: CrossAxisAlignment.center, // 子组件水平居中
        children: [
          Image.network("https://xxx.png", width: 80),
          SizedBox(height: 10), // 间距组件
          Text("图片描述"),
        ],
      );
    • 容器布局(Container):控制尺寸、颜色、边距、圆角等

      dart 复制代码
      Container(
        width: 200,
        height: 200,
        color: Colors.grey[200], // 背景色
        padding: EdgeInsets.all(16), // 内边距
        margin: EdgeInsets.symmetric(horizontal: 20), // 外边距(水平方向)
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(10), // 圆角
          boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 3)], // 阴影
        ),
        child: Text("容器内的文本"),
      );
    • 弹性布局(Flex/Expanded):适配屏幕宽度

      dart 复制代码
      Row(
        children: [
          Expanded( // 占满剩余宽度
            flex: 1, // 权重(总权重 1+2=3,占 1/3)
            child: Container(color: Colors.red),
          ),
          Expanded(
            flex: 2, // 占 2/3
            child: Container(color: Colors.blue),
          ),
        ],
      );
  3. 资源引入(本地图片/字体)

    • 本地图片:
      1. 在项目根目录创建 assets/images 文件夹,放入图片(比如 avatar.png);

      2. 打开 pubspec.yaml,配置资源路径:

        yaml 复制代码
        flutter:
          assets:
            - assets/images/avatar.png # 单个图片
            # 或 - assets/images/ # 整个文件夹
      3. 代码中使用:Image.asset("assets/images/avatar.png")

本周练习:搭建「登录页面」

需求:包含「用户名输入框」「密码输入框」「登录按钮」「忘记密码文本」,布局居中,有适当边距和圆角。

参考代码框架:

dart 复制代码
class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: EdgeInsets.symmetric(horizontal: 30),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 标题
            Text("用户登录", style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold)),
            SizedBox(height: 40),
            // 用户名输入框
            TextField(
              decoration: InputDecoration(
                hintText: "请输入用户名",
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
              ),
            ),
            SizedBox(height: 16),
            // 密码输入框
            TextField(
              obscureText: true, // 密码隐藏
              decoration: InputDecoration(
                hintText: "请输入密码",
                border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
              ),
            ),
            SizedBox(height: 8),
            // 忘记密码(右对齐)
            Align(
              alignment: Alignment.centerRight,
              child: TextButton(onPressed: () {}, child: Text("忘记密码?")),
            ),
            SizedBox(height: 30),
            // 登录按钮(占满宽度)
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () {},
                style: ElevatedButton.styleFrom(
                  padding: EdgeInsets.symmetric(vertical: 14),
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
                ),
                child: Text("登录", style: TextStyle(fontSize: 18)),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

第3周:交互事件与状态管理基础(让页面「活」起来)

目标:掌握点击、输入等交互,理解「状态管理」的核心,能用 setState 更新 UI

核心知识点:
  1. 交互事件处理

    • 按钮点击:onPressed 回调(无参数);

    • 列表点击:GestureDetector(监听手势,如点击、长按、滑动);

      dart 复制代码
      // GestureDetector 包裹组件,添加点击事件
      GestureDetector(
        onTap: () {
          print("组件被点击了");
        },
        onLongPress: () {
          print("组件被长按了");
        },
        child: Container(width: 100, height: 100, color: Colors.green),
      );
  2. 状态管理基础(setState)

    • 适用场景:简单页面(单个组件内的状态变化,比如计数器、开关、输入框内容);

    • 核心逻辑:状态变量放在 State 类中,修改状态时必须调用 setState,通知 Flutter 重新构建 UI;

    • 示例:实现「开关按钮控制文本显示」

      dart 复制代码
      class SwitchDemo extends StatefulWidget {
        @override
        State<SwitchDemo> createState() => _SwitchDemoState();
      }
      
      class _SwitchDemoState extends State<SwitchDemo> {
        bool _isOn = false; // 状态变量:开关是否打开
      
        @override
        Widget build(BuildContext context) {
          return Row(
            children: [
              Switch(
                value: _isOn,
                onChanged: (value) {
                  // 点击开关,更新状态
                  setState(() {
                    _isOn = value;
                  });
                },
              ),
              Text(_isOn ? "开关已打开" : "开关已关闭"),
            ],
          );
        }
      }
  3. 表单验证(登录页优化)

    给登录页添加表单验证(用户名不能为空、密码长度≥6):

    dart 复制代码
    class LoginPage extends StatefulWidget {
      @override
      State<LoginPage> createState() => _LoginPageState();
    }
    
    class _LoginPageState extends State<LoginPage> {
      final _formKey = GlobalKey<FormState>(); // 表单key,用于验证
      String _username = "";
      String _password = "";
    
      // 提交表单
      void _submitForm() {
        if (_formKey.currentState!.validate()) {
          _formKey.currentState!.save(); // 保存输入值
          print("用户名:$_username,密码:$_password");
          // 这里可以添加登录逻辑
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Padding(
            padding: EdgeInsets.symmetric(horizontal: 30),
            child: Form( // 表单组件
              key: _formKey,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("用户登录", style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold)),
                  SizedBox(height: 40),
                  // 用户名输入框(带验证)
                  TextFormField(
                    decoration: InputDecoration(
                      hintText: "请输入用户名",
                      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
                    ),
                    validator: (value) { // 验证逻辑
                      if (value == null || value.isEmpty) {
                        return "用户名不能为空";
                      }
                      return null; // 验证通过
                    },
                    onSaved: (value) { // 保存输入值
                      _username = value!;
                    },
                  ),
                  SizedBox(height: 16),
                  // 密码输入框(带验证)
                  TextFormField(
                    obscureText: true,
                    decoration: InputDecoration(
                      hintText: "请输入密码",
                      border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
                    ),
                    validator: (value) {
                      if (value == null || value.length < 6) {
                        return "密码长度不能少于6位";
                      }
                      return null;
                    },
                    onSaved: (value) {
                      _password = value!;
                    },
                  ),
                  SizedBox(height: 30),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: _submitForm, // 点击提交表单
                      child: Text("登录"),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
本周练习:完善登录页
  1. 实现「记住密码」开关(状态持久化到本地,用 SharedPreferences,提前预习第4周内容);
  2. 点击登录按钮后,显示加载状态(按钮禁用 + 加载动画);
  3. 搭建「列表页面」(用 ListView 显示多条数据,点击列表项跳转到详情页)。

第4周:路由导航与列表组件(页面跳转 + 数据展示)

目标:掌握页面跳转、传参,能用 ListView 展示列表数据

核心知识点:
  1. 路由导航(页面跳转)

    • 基础路由(直接跳转):

      dart 复制代码
      // 跳转到新页面
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => DetailPage(), // 目标页面
        ),
      );
      
      // 从新页面返回上一页
      Navigator.pop(context);
    • 路由传参(带数据跳转):

      dart 复制代码
      // 1. 目标页面(接收参数)
      class DetailPage extends StatelessWidget {
        final String username; // 接收的参数
      
        // 构造函数接收参数
        const DetailPage({super.key, required this.username});
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(title: Text("详情页")),
            body: Center(child: Text("欢迎你,$username!")),
          );
        }
      }
      
      // 2. 跳转时传参
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => DetailPage(username: _username), // 传入参数
        ),
      );
    • 命名路由(更简洁,适合多页面项目):

      1. 在 MyApp 中配置路由表:

        dart 复制代码
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              routes: {
                "/": (context) => LoginPage(), // 根路由(首页)
                "/detail": (context) => DetailPage(), // 命名路由
              },
            );
          }
        }
      2. 跳转时使用命名路由:

        dart 复制代码
        // 传参(用 arguments)
        Navigator.pushNamed(
          context,
          "/detail",
          arguments: _username, // 传入参数
        );
        
        // 目标页面接收参数
        class DetailPage extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            final String username = ModalRoute.of(context)!.settings.arguments as String;
            return Scaffold(appBar: AppBar(title: Text("详情页")), body: Center(child: Text("欢迎 $username")));
          }
        }
  2. 列表组件(ListView)

    • 静态列表(固定数据):

      dart 复制代码
      ListView(
        children: [
          ListTile(
            leading: Icon(Icons.person), // 左侧图标
            title: Text("张三"), // 标题
            subtitle: Text("13800138000"), // 副标题
            trailing: Icon(Icons.arrow_right), // 右侧图标
            onTap: () { // 点击事件
              Navigator.pushNamed(context, "/detail", arguments: "张三");
            },
          ),
          Divider(height: 1), // 分割线
          ListTile(
            leading: Icon(Icons.person),
            title: Text("李四"),
            subtitle: Text("13900139000"),
            trailing: Icon(Icons.arrow_right),
            onTap: () {
              Navigator.pushNamed(context, "/detail", arguments: "李四");
            },
          ),
        ],
      );
    • 动态列表(从数据数组生成):

      dart 复制代码
      class UserListPage extends StatelessWidget {
        // 模拟数据
        final List<Map<String, String>> users = [
          {"name": "张三", "phone": "13800138000"},
          {"name": "李四", "phone": "13900139000"},
          {"name": "王五", "phone": "13700137000"},
        ];
      
        @override
        Widget build(BuildContext context) {
          return ListView.builder(
            itemCount: users.length, // 列表项数量
            itemBuilder: (context, index) { // 构建每个列表项
              final user = users[index];
              return ListTile(
                leading: Icon(Icons.person),
                title: Text(user["name"]!),
                subtitle: Text(user["phone"]!),
                onTap: () {
                  Navigator.pushNamed(context, "/detail", arguments: user["name"]);
                },
              );
            },
          );
        }
      }
本周练习:实现「联系人列表 + 详情页」
  1. 搭建联系人列表页(动态生成 10 条模拟数据);
  2. 点击列表项跳转到详情页,展示联系人姓名、电话;
  3. 详情页添加「返回」按钮,点击返回列表页;
  4. 给列表添加下拉刷新(RefreshIndicator)和上拉加载更多功能。

第三阶段:核心进阶(第5-8周)------ 掌握「实战必备」技能

第5周:网络请求与 JSON 解析(对接后端数据)

目标:能用 Dio 发起网络请求,解析 JSON 数据并展示到 UI

核心知识点:
  1. 网络请求库:Dio

    • 步骤1:添加依赖(pubspec.yaml):

      yaml 复制代码
      dependencies:
        flutter:
          sdk: flutter
        dio: ^5.4.0 # 添加 Dio 依赖(去 pub.dev 查最新版本)
    • 步骤2:执行 flutter pub get 安装依赖;

    • 步骤3:发起 GET/POST 请求:

      dart 复制代码
      import 'package:dio/dio.dart';
      
      // 发起 GET 请求(获取新闻列表)
      Future<List<News>> fetchNews() async {
        final dio = Dio();
        try {
          // 模拟接口(实际项目替换成后端真实接口)
          final response = await dio.get("https://api.example.com/news");
          if (response.statusCode == 200) {
            // 解析 JSON 数据(见下面步骤)
            List<dynamic> data = response.data["data"];
            return data.map((item) => News.fromJson(item)).toList();
          } else {
            throw Exception("请求失败");
          }
        } catch (e) {
          throw Exception("请求异常:$e");
        }
      }
  2. JSON 解析(模型类 + fromJson/toJson)

    • 问题:直接操作 JSON 字典(Map)容易出错,需转换成模型类;

    • 步骤1:定义模型类(以新闻为例):

      dart 复制代码
      class News {
        final String id;
        final String title;
        final String content;
        final String imageUrl;
        final String time;
      
        // 构造函数
        News({
          required this.id,
          required this.title,
          required this.content,
          required this.imageUrl,
          required this.time,
        });
      
        // 从 JSON 转换为模型对象
        factory News.fromJson(Map<String, dynamic> json) {
          return News(
            id: json["id"],
            title: json["title"],
            content: json["content"],
            imageUrl: json["image_url"],
            time: json["time"],
          );
        }
      
        // 从模型对象转换为 JSON(可选,用于提交数据)
        Map<String, dynamic> toJson() {
          return {
            "id": id,
            "title": title,
            "content": content,
            "image_url": imageUrl,
            "time": time,
          };
        }
      }
  3. 结合 UI 展示网络数据(FutureBuilder)

    用 FutureBuilder 处理异步请求,展示加载中、成功、失败状态:

    dart 复制代码
    class NewsListPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("新闻列表")),
          body: FutureBuilder<List<News>>(
            future: fetchNews(), // 异步请求函数
            builder: (context, snapshot) {
              // 加载中
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(child: CircularProgressIndicator());
              }
              // 请求失败
              else if (snapshot.hasError) {
                return Center(child: Text("加载失败:${snapshot.error}"));
              }
              // 请求成功
              else if (snapshot.hasData) {
                final newsList = snapshot.data!;
                return ListView.builder(
                  itemCount: newsList.length,
                  itemBuilder: (context, index) {
                    final news = newsList[index];
                    return ListTile(
                      leading: Image.network(news.imageUrl, width: 60, height: 60, fit: BoxFit.cover),
                      title: Text(news.title, maxLines: 1, overflow: TextOverflow.ellipsis),
                      subtitle: Text(news.time, style: TextStyle(color: Colors.grey)),
                      onTap: () {
                        // 跳转到新闻详情页
                        Navigator.pushNamed(context, "/newsDetail", arguments: news);
                      },
                    );
                  },
                );
              }
              // 无数据
              else {
                return Center(child: Text("暂无数据"));
              }
            },
          ),
        );
      }
    }
本周练习:实现「简易新闻App」
  1. 用 Dio 请求真实接口(推荐:聚合数据新闻接口,需申请 API Key);
  2. 解析 JSON 数据,展示新闻列表(包含标题、图片、时间);
  3. 点击列表项跳转到新闻详情页,展示完整内容;
  4. 处理异常情况(无网络、接口报错),显示友好提示。

第6周:状态管理进阶(Provider)

目标:解决「跨组件传参」问题,掌握工业级状态管理方案 Provider

核心知识点:
  1. 为什么需要 Provider?

    • 问题:当 App 复杂时,setState 只能管理单个组件的状态,跨组件(比如首页和详情页共享用户信息)传参困难;
    • 解决方案:用 Provider 把「共享状态」抽离出来,全局管理,任何组件都能访问和修改。
  2. Provider 核心概念

    • ChangeNotifier:状态模型类,继承它可以监听状态变化;
    • ChangeNotifierProvider:提供器,把状态模型暴露给子组件;
    • Consumer:消费者,从提供器中获取状态,状态变化时重新构建;
    • Provider.of:另一种获取状态的方式(适合不需要重建的场景)。
  3. Provider 实战:共享用户登录状态

    步骤1:添加依赖(pubspec.yaml):

    yaml 复制代码
    dependencies:
      provider: ^6.1.1 # Provider 依赖

    步骤2:定义状态模型类(UserModel):

    dart 复制代码
    import 'package:flutter/foundation.dart';
    
    class UserModel extends ChangeNotifier {
      String? _username; // 共享状态:用户名
    
      // 获取用户名
      String? get username => _username;
    
      // 登录(修改状态)
      void login(String username) {
        _username = username;
        notifyListeners(); // 通知所有消费者:状态变了,重新构建 UI
      }
    
      // 退出登录
      void logout() {
        _username = null;
        notifyListeners();
      }
    }

    步骤3:在根组件中提供状态:

    dart 复制代码
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // 用 ChangeNotifierProvider 包裹 MaterialApp,全局提供 UserModel
        return ChangeNotifierProvider(
          create: (context) => UserModel(), // 创建状态模型
          child: MaterialApp(
            routes: {
              "/": (context) => LoginPage(),
              "/home": (context) => HomePage(),
            },
          ),
        );
      }
    }

    步骤4:在登录页修改状态:

    dart 复制代码
    class LoginPage extends StatefulWidget {
      @override
      State<LoginPage> createState() => _LoginPageState();
    }
    
    class _LoginPageState extends State<LoginPage> {
      final _usernameController = TextEditingController();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Padding(
            padding: EdgeInsets.symmetric(horizontal: 30),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextField(
                  controller: _usernameController,
                  hintText: "请输入用户名",
                ),
                SizedBox(height: 20),
                ElevatedButton(
                  onPressed: () {
                    String username = _usernameController.text;
                    if (username.isNotEmpty) {
                      // 获取 UserModel,调用 login 方法修改状态
                      Provider.of<UserModel>(context, listen: false).login(username);
                      // 跳转到首页
                      Navigator.pushReplacementNamed(context, "/home");
                    }
                  },
                  child: Text("登录"),
                ),
              ],
            ),
          ),
        );
      }
    }

    步骤5:在首页获取状态:

    dart 复制代码
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("首页"),
            actions: [
              IconButton(
                icon: Icon(Icons.logout),
                onPressed: () {
                  // 退出登录
                  Provider.of<UserModel>(context, listen: false).logout();
                  // 返回登录页
                  Navigator.pushReplacementNamed(context, "/");
                },
              ),
            ],
          ),
          body: Center(
            // 用 Consumer 监听状态变化,状态变了会重新构建
            child: Consumer<UserModel>(
              builder: (context, userModel, child) {
                return Text("欢迎你,${userModel.username}!");
              },
            ),
          ),
        );
      }
    }
本周练习:优化新闻App
  1. 用 Provider 管理「新闻列表数据」,实现列表页和详情页共享数据;
  2. 新增「收藏新闻」功能,收藏状态用 Provider 管理,跨页面同步;
  3. 实现「夜间模式切换」,主题状态用 Provider 管理,全局生效。

第7周:本地存储(SharedPreferences + SQLite)

目标:掌握轻量级存储和数据库存储,实现数据持久化

核心知识点:
  1. 轻量级存储:SharedPreferences

    • 适用场景:存储少量简单数据(如用户名、开关状态、token);

    • 步骤1:添加依赖:

      yaml 复制代码
      dependencies:
        shared_preferences: ^2.2.2
    • 步骤2:使用示例(存储/读取用户名):

      dart 复制代码
      import 'package:shared_preferences/shared_preferences.dart';
      
      // 存储用户名
      Future<void> saveUsername(String username) async {
        final prefs = await SharedPreferences.getInstance();
        await prefs.setString("username", username); // 键值对存储
      }
      
      // 读取用户名
      Future<String?> getUsername() async {
        final prefs = await SharedPreferences.getInstance();
        return prefs.getString("username"); // 根据键读取
      }
      
      // 删除用户名
      Future<void> removeUsername() async {
        final prefs = await SharedPreferences.getInstance();
        await prefs.remove("username");
      }
  2. 数据库存储:SQLite(sqflite)

    • 适用场景:存储大量结构化数据(如收藏的新闻、联系人列表);

    • 步骤1:添加依赖:

      yaml 复制代码
      dependencies:
        sqflite: ^2.3.0
        path: ^1.8.3 # 用于拼接数据库路径
    • 步骤2:使用示例(创建新闻收藏表,增删改查):

      dart 复制代码
      import 'package:sqflite/sqflite.dart';
      import 'package:path/path.dart';
      import 'package:news_app/models/news.dart';
      
      class DatabaseHelper {
        static final DatabaseHelper instance = DatabaseHelper._init();
        static Database? _database;
      
        DatabaseHelper._init();
      
        // 获取数据库实例
        Future<Database> get database async {
          if (_database != null) return _database!;
          _database = await _initDB("favorites.db"); // 数据库文件名
          return _database!;
        }
      
        // 初始化数据库
        Future<Database> _initDB(String filePath) async {
          final dbPath = await getDatabasesPath();
          final path = join(dbPath, filePath);
      
          // 创建数据库表(新闻收藏表)
          return await openDatabase(path, version: 1, onCreate: _createDB);
        }
      
        // 创建表
        Future<void> _createDB(Database db, int version) async {
          await db.execute('''
            CREATE TABLE favorites (
              id TEXT PRIMARY KEY,
              title TEXT NOT NULL,
              content TEXT NOT NULL,
              imageUrl TEXT NOT NULL,
              time TEXT NOT NULL
            )
          ''');
        }
      
        // 新增收藏(插入数据)
        Future<void> insertFavorite(News news) async {
          final db = await instance.database;
          await db.insert("favorites", news.toJson());
        }
      
        // 查询所有收藏(读取数据)
        Future<List<News>> getFavorites() async {
          final db = await instance.database;
          final maps = await db.query("favorites");
          return maps.map((map) => News.fromJson(map)).toList();
        }
      
        // 删除收藏(删除数据)
        Future<void> deleteFavorite(String id) async {
          final db = await instance.database;
          await db.delete("favorites", where: "id = ?", whereArgs: [id]);
        }
      }
本周练习:完善新闻App的本地存储功能
  1. 用 SharedPreferences 存储「登录状态」和「夜间模式开关状态」,App 重启后保持;
  2. 用 SQLite 实现「新闻收藏」功能:点击收藏按钮保存新闻到数据库,在「我的收藏」页面展示,支持取消收藏;
  3. 实现「离线查看收藏新闻」功能,无网络时也能查看。

第8周:常用插件与原生交互

目标:掌握 Flutter 常用插件的使用,了解 Flutter 与原生(Android/iOS)的交互方式

核心知识点:
  1. 常用插件推荐(直接用 pub.dev 搜索安装)

    插件名称 作用 使用场景
    fluttertoast 显示提示框(Toast) 操作成功/失败提示(如「收藏成功」)
    image_picker 选择图片/拍照 头像上传、发布动态
    url_launcher 打开链接、拨打电话 跳转到网页、拨打客服电话
    share_plus 分享文本/图片 分享新闻到微信、QQ
    flutter_svg 显示 SVG 图片 图标、Logo(清晰度更高)

    示例:使用 fluttertoast 显示提示:

    dart 复制代码
    // 1. 添加依赖:fluttertoast: ^8.2.2
    // 2. 导入:import 'package:fluttertoast/fluttertoast.dart';
    // 3. 使用:
    Fluttertoast.showToast(
      msg: "收藏成功!",
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.BOTTOM, // 显示位置(底部)
      timeInSecForIosWeb: 1,
    );
  2. Flutter 与原生交互(MethodChannel)

    • 小白解释:Flutter 调用原生(Android Java/Kotlin、iOS Swift/OC)的方法,或原生调用 Flutter 方法;

    • 简单示例:Flutter 调用原生的「获取设备型号」方法:

      dart 复制代码
      // Flutter 端代码
      import 'package:flutter/services.dart';
      
      class NativeUtils {
        static const MethodChannel _channel = MethodChannel("com.example.news_app/native");
      
        // 调用原生方法获取设备型号
        static Future<String?> getDeviceModel() async {
          try {
            final String? result = await _channel.invokeMethod("getDeviceModel");
            return result;
          } on PlatformException catch (e) {
            return "获取失败:${e.message}";
          }
        }
      }
      
      // 调用
      NativeUtils.getDeviceModel().then((model) {
        print("设备型号:$model");
      });

      Android 原生端(Kotlin):

      kotlin 复制代码
      // MainActivity.kt
      class MainActivity : FlutterActivity() {
        private val CHANNEL = "com.example.news_app/native"
      
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
          super.configureFlutterEngine(flutterEngine)
          MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getDeviceModel") {
              val model = Build.MODEL // Android 原生获取设备型号
              result.success(model)
            } else {
              result.notImplemented()
            }
          }
        }
      }
本周练习:给新闻App添加插件功能
  1. 添加「分享新闻」功能(用 share_plus),支持分享新闻标题和链接;
  2. 添加「头像上传」功能(用 image_picker),选择本地图片或拍照,显示预览;
  3. 添加「拨打电话」功能(用 url_launcher),在「关于我们」页面添加客服电话,点击拨打。

第四阶段:项目实战(第9-10周)------ 独立开发完整App

项目主题:TodoList 待办清单App(适合新手,覆盖所有核心技能)

项目需求:
  1. 功能模块:

    • 登录/注册(用 SharedPreferences 存储用户信息);
    • 待办列表(添加、编辑、删除、标记完成/未完成);
    • 待办分类(工作、学习、生活,用 SQLite 存储);
    • 数据统计(完成率、今日待办数,用 Provider 管理状态);
    • 设置页面(夜间模式、清除缓存、关于我们)。
  2. 技术栈:

    • 状态管理:Provider;
    • 本地存储:SharedPreferences(用户信息)+ SQLite(待办数据);
    • 路由:命名路由;
    • 插件:fluttertoast(提示)、share_plus(分享待办)。
开发步骤(第9周:搭建架构 + 核心功能;第10周:完善功能 + 调试优化):
  1. 第9周 Day1-2:项目架构设计

    • 创建项目结构:

      复制代码
      lib/
      ├── main.dart          # 入口文件
      ├── models/            # 模型类(User、Todo、Category)
      ├── providers/         # Provider 状态管理(UserProvider、TodoProvider)
      ├── services/          # 服务类(数据库、本地存储、网络)
      ├── screens/           # 页面(登录、注册、首页、待办详情、设置)
      ├── widgets/           # 自定义组件(待办项卡片、分类标签)
      └── utils/             # 工具类(常量、工具函数)
    • 配置路由、主题、Provider 全局状态。

  2. 第9周 Day3-5:核心功能开发

    • 登录/注册页面(表单验证 + 本地存储用户信息);
    • 待办列表页面(SQLite 增删改查 + Provider 状态更新);
    • 待办添加/编辑页面(表单 + 分类选择)。
  3. 第10周 Day1-3:完善功能

    • 数据统计页面(计算完成率、今日待办数);
    • 设置页面(夜间模式切换、清除缓存);
    • 添加插件功能(分享待办、提示框)。
  4. 第10周 Day4-7:调试优化

    • 修复 Bug(如数据同步、状态刷新问题);
    • UI 优化(适配不同屏幕、美化组件);
    • 性能优化(减少不必要的重建、数据库索引优化)。
项目交付:
  • 可运行的 App 测试包(Android APK / iOS IPA);
  • 完整的源代码(含注释);
  • 简单的开发文档(记录核心功能实现思路)。

第五阶段:底层原理(第11周)------ 知其然也知其所以然

核心目标:理解 Flutter 底层逻辑,能排查常见问题

核心知识点:
  1. Flutter 渲染原理(三棵树)

    • 小白解释:Flutter 渲染 UI 时,会创建三棵树,层层递进将 Widget 转换成屏幕上的像素;
    • 三棵树关系:
      • Widget 树:描述 UI 结构(不可变,如 Text、Container);
      • Element 树:Widget 的实例(可变,负责管理 Widget 的生命周期,如创建、更新、销毁);
      • RenderObject 树:负责布局和绘制(计算组件位置、大小,绘制像素)。
    • 渲染流程:Widget 树 → Element 树 → RenderObject 树 → 屏幕渲染。
  2. 热重载原理

    • 核心:Flutter 会保留当前 App 的状态(Element 树和 RenderObject 树),只重新构建修改后的 Widget 树,然后更新 Element 树和 RenderObject 树,无需重启 App,所以速度快;
    • 适用场景:修改 UI 结构、样式、逻辑代码(不修改状态类、路由配置等);
    • 不生效场景:修改 main 函数、状态类的构造函数、全局常量等,需热重启。
  3. Widget 生命周期

    • 无状态组件(StatelessWidget):只有 build 方法,每次重建都会重新调用;

    • 有状态组件(StatefulWidget):State 类的生命周期:

      复制代码
      createState() → initState()(初始化状态)→ didChangeDependencies()(依赖变化)→ build()(构建UI)→ didUpdateWidget()(Widget 重新构建)→ dispose()(组件销毁,释放资源)
  4. 常见问题排查思路

    • UI 不更新:检查是否调用 setState(有状态组件)或 notifyListeners(Provider);
    • 布局错乱:检查父组件的约束(如 Container 的 width/height、布局组件的对齐方式);
    • 性能卡顿:避免在 build 方法中创建对象(如 TextStyle 应抽成常量)、减少不必要的重建(用 const 构造函数、Consumer 局部刷新);
    • 网络请求失败:检查接口地址、参数、权限(如 iOS 需开启网络权限)。
本周任务:
  1. 阅读 Flutter 官方文档的「底层原理」部分:Flutter 架构概览;
  2. 分析自己的 TodoList 项目,梳理核心组件的生命周期;
  3. 故意在项目中引入一个 Bug(如 UI 不更新、布局错乱),用底层原理知识排查并修复。

第六阶段:综合提升(第12周)------ 进阶与发布

核心目标:掌握性能优化、打包发布,了解 Flutter 进阶特性

核心知识点:
  1. 性能优化技巧

    • 减少 Widget 重建:
      • 用 const 构造函数(如 const Text("固定文本"));
      • 抽离不变的 Widget 为单独组件;
      • Provider 中用 Consumer 局部刷新,而非 Provider.of 全局刷新。
    • 列表性能优化:
      • 用 ListView.builder(懒加载,只构建可见项);
      • 用 itemExtent 指定列表项高度,减少布局计算;
      • 复杂列表项用 RepaintBoundary 隔离重绘区域。
    • 图片优化:
      • 压缩图片尺寸(避免加载过大图片);
      • 用 CachedNetworkImage 缓存网络图片(减少重复请求)。
  2. 打包发布

    • Android 打包(生成 APK/AAB):
      1. 生成签名文件(key.jks):用 Android Studio 或命令行生成;
      2. 配置 android/app/build.gradle:添加签名信息;
      3. 执行打包命令:flutter build appbundle(AAB,Google Play 推荐)或 flutter build apk(APK)。
    • iOS 打包(生成 IPA):
      1. 配置 Xcode 项目(Bundle ID、签名证书);
      2. 选择模拟器/真机,执行 flutter build ios;
      3. 用 Xcode 归档(Archive)并上传到 App Store Connect。
  3. Flutter 进阶特性(可选)

    • 自定义 Widget:继承 StatelessWidget/StatefulWidget,重写 build 方法;
    • 动画:用 AnimatedContainer、AnimationController 实现简单动画;
    • Flutter 3.x+ 新特性:Material 3 主题、GoRouter(路由管理新方案)、Riverpod(Provider 升级版)。
本周任务:
  1. 优化 TodoList 项目性能(至少实现 2 个优化点);
  2. 打包生成 Android APK 和 iOS IPA 测试包;
  3. 尝试接入 Material 3 主题,美化 App 界面;
  4. 总结学习心得,梳理知识体系。

小白学习小贴士

  1. 多动手,少死记:Flutter 是实战型技术,每个知识点都要跟着敲代码,跑通示例,不要只看不动手;
  2. 遇到问题先自查:终端报错信息 → 官方文档 → 搜索引擎(优先 Stack Overflow、掘金);
  3. 循序渐进,不急于求成:底层原理可以先放一放,先掌握基础和实战技能,有了项目经验再回头看原理,更容易理解;
  4. 关注社区动态:Flutter 更新快,关注 Flutter 官方博客、掘金 Flutter 专栏,了解最新特性和最佳实践。

按照这个计划学习,12周后你将从「小白」成长为「能独立开发并发布 Flutter App 的开发者」,后续可以根据兴趣深入学习某一方向(如 Flutter 桌面端、Web 端、游戏开发等)!如果学习过程中遇到具体问题,随时可以针对性提问~

相关推荐
jay神36 分钟前
基于 YOLOv8 的轴承缺陷检测(Web 版)
前端·人工智能·yolo
jay神44 分钟前
基于 YOLOv8 的森林火灾检测系统(Web 版)
前端·yolo
code2cat1 小时前
HTML基础篇之第一张网页:从index.html到浏览器里的任务清单
前端·html
miss2 小时前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan2 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真2 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment2 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan2 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么3 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技3 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue