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 方法;
    • 动画:用 AnimatedContainerAnimationController 实现简单动画;
    • 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 端、游戏开发等)!如果学习过程中遇到具体问题,随时可以针对性提问~

相关推荐
国科安芯2 小时前
AS32A601型MCU芯片flash模块的擦除和编程
java·linux·前端·单片机·嵌入式硬件·fpga开发·安全性测试
IT_陈寒2 小时前
【SpringBoot 3.2实战】10倍性能优化的5个冷门技巧,90%开发者都不知道!
前端·人工智能·后端
n***i952 小时前
前端技术的反向演进:去框架化浪潮下的轻量化与原生能力回归
前端
幸运小圣2 小时前
defineAsyncComponent【Vue3】
前端·javascript·vue.js
一个很帅的帅哥2 小时前
webpack开发极简指南
前端·webpack·node.js
拾忆,想起2 小时前
Dubbo服务访问控制(ACL)完全指南:从IP黑白名单到自定义安全策略
前端·网络·网络协议·tcp/ip·微服务·php·dubbo
fruge2 小时前
Webpack 5 优化指南:分包策略、缓存配置及构建速度提升 60%
前端·缓存·webpack
相逢一笑与君行2 小时前
css使用grid布局实现网格(表格),动态调整行高,列宽,整体缩放,插入行,列,删除行,列
前端·css·react
拾忆,想起2 小时前
Dubbo序列化性能优化实战:从协议选型到极致调优
前端·微服务·性能优化·架构·dubbo·safari