Flutter 从入门到深入:小白专属教学计划
核心目标
从「零编程基础」或「非Flutter开发背景」出发,逐步掌握 Flutter 开发全流程:
- 夯实 Dart 语法与 Flutter 基础组件;
- 熟练运用实战核心技能(状态管理、网络请求、本地存储等);
- 能独立开发完整 App 并打包发布;
- 理解 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 项目,搞定「环境变量 + 编辑器 + 模拟器/真机」
详细步骤:
- 准备工具 :
- 编辑器:推荐 VS Code(轻量,小白友好),安装「Flutter」「Dart」插件;
- 环境依赖:
- Windows:安装 Git + Flutter SDK(解压到无中文路径);
- Mac:安装 Xcode(需Apple ID,用于iOS开发)+ Flutter SDK;
- Linux:安装 Git + Flutter SDK。
- 配置环境变量 :
- 核心:将 Flutter 的
bin目录路径添加到系统环境变量(比如 Windows 是此电脑→属性→高级系统设置→环境变量→Path); - 验证:打开终端(Windows CMD/PowerShell,Mac/Linux 终端),输入
flutter --version,能显示版本则成功。
- 核心:将 Flutter 的
- 解决依赖问题 :
运行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)。
- 测试环境 :
终端输入flutter create my_first_app(创建项目),进入项目目录cd my_first_app,运行flutter run,等待模拟器/真机启动项目,看到默认的「Flutter Demo」页面则成功!
Day2-3:Dart 语法基础(Flutter 的「编程语言」)
目标:掌握 Dart 核心语法,能看懂简单代码,不用死记硬背,理解「怎么用」即可
核心知识点 + 小白解释 + 代码示例:
-
变量与数据类型
-
小白解释:变量就是「存放数据的盒子」,数据类型决定「盒子里能放什么」;
-
关键语法:
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; // 编译时确定值(常量)
-
-
函数(一段可重复执行的代码)
-
小白解释:比如「计算两个数的和」,写一次函数,以后直接调用,不用重复写代码;
-
关键语法:
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 }
-
-
条件判断与循环
-
小白解释:让程序「做选择」(条件判断)或「重复做事」(循环);
-
关键语法:
dartvoid 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"); // 输出每个水果 } }
-
-
面向对象(类与对象)
-
小白解释:把「事物」抽象成「类」(模板),再用模板创建「对象」(具体实例),比如「人」是类,「张三」是对象;
-
关键语法:
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 是什么」
核心知识点:
-
Flutter 是什么?
- 小白解释:一个「跨平台 App 开发框架」,写一套代码,能同时运行在 iOS 和 Android 上,还能做 Web/桌面端;
- 核心优势:性能接近原生(因为直接编译成机器码,不是网页套壳)、UI 一致性强、开发效率高(热重载)。
-
Widget 核心思想
- 小白解释:Flutter 里「万物皆组件」,界面上的所有元素(文本、图片、按钮、布局)都是 Widget;
- 关键特点:
- 不可变(Immutable):Widget 一旦创建,属性不能改;要更新 UI,必须重新创建 Widget;
- 组合优于继承:通过「嵌套 Widget」实现复杂 UI,而不是继承现有 Widget。
-
第一个 Flutter 项目解析
打开之前创建的
my_first_app,看lib/main.dart(核心文件):dartimport '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:练习与巩固
- 修改
my_first_app:- 把标题改成自己的名字;
- 把悬浮按钮的图标换成
Icons.star(星星); - 把文本颜色改成红色。
- 新建一个 Dart 文件
lib/hello.dart,写一个函数,接收一个名字,返回「Hello, 名字!」,在main.dart中调用并打印。 - 遇到问题怎么办?
- 优先看终端报错信息(红色文字),按提示排查;
- 查 Flutter 官方文档:Flutter Docs(可切换中文);
- 搜 Stack Overflow 或 掘金/CSDN 相关问题。
第二阶段:基础夯实(第2-4周)------ 掌握「搭界面 + 做交互」的核心能力
第2周:Widget 体系与基础布局(搭出漂亮界面)
目标:掌握常用基础 Widget 和布局技巧,能独立搭建静态页面
核心知识点 + 代码示例:
-
基础组件(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: "请输入用户名") -
布局组件(组织多个组件的排列)
核心: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):控制尺寸、颜色、边距、圆角等
dartContainer( 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):适配屏幕宽度
dartRow( children: [ Expanded( // 占满剩余宽度 flex: 1, // 权重(总权重 1+2=3,占 1/3) child: Container(color: Colors.red), ), Expanded( flex: 2, // 占 2/3 child: Container(color: Colors.blue), ), ], );
-
-
资源引入(本地图片/字体)
- 本地图片:
-
在项目根目录创建
assets/images文件夹,放入图片(比如avatar.png); -
打开
pubspec.yaml,配置资源路径:yamlflutter: assets: - assets/images/avatar.png # 单个图片 # 或 - assets/images/ # 整个文件夹 -
代码中使用:
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
核心知识点:
-
交互事件处理
-
按钮点击:
onPressed回调(无参数); -
列表点击:
GestureDetector(监听手势,如点击、长按、滑动);dart// GestureDetector 包裹组件,添加点击事件 GestureDetector( onTap: () { print("组件被点击了"); }, onLongPress: () { print("组件被长按了"); }, child: Container(width: 100, height: 100, color: Colors.green), );
-
-
状态管理基础(setState)
-
适用场景:简单页面(单个组件内的状态变化,比如计数器、开关、输入框内容);
-
核心逻辑:状态变量放在
State类中,修改状态时必须调用setState,通知 Flutter 重新构建 UI; -
示例:实现「开关按钮控制文本显示」
dartclass 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 ? "开关已打开" : "开关已关闭"), ], ); } }
-
-
表单验证(登录页优化)
给登录页添加表单验证(用户名不能为空、密码长度≥6):
dartclass 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("登录"), ), ), ], ), ), ), ); } }
本周练习:完善登录页
- 实现「记住密码」开关(状态持久化到本地,用
SharedPreferences,提前预习第4周内容); - 点击登录按钮后,显示加载状态(按钮禁用 + 加载动画);
- 搭建「列表页面」(用
ListView显示多条数据,点击列表项跳转到详情页)。
第4周:路由导航与列表组件(页面跳转 + 数据展示)
目标:掌握页面跳转、传参,能用 ListView 展示列表数据
核心知识点:
-
路由导航(页面跳转)
-
基础路由(直接跳转):
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), // 传入参数 ), ); -
命名路由(更简洁,适合多页面项目):
-
在
MyApp中配置路由表:dartclass MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( routes: { "/": (context) => LoginPage(), // 根路由(首页) "/detail": (context) => DetailPage(), // 命名路由 }, ); } } -
跳转时使用命名路由:
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"))); } }
-
-
-
列表组件(ListView)
-
静态列表(固定数据):
dartListView( 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: "李四"); }, ), ], ); -
动态列表(从数据数组生成):
dartclass 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"]); }, ); }, ); } }
-
本周练习:实现「联系人列表 + 详情页」
- 搭建联系人列表页(动态生成 10 条模拟数据);
- 点击列表项跳转到详情页,展示联系人姓名、电话;
- 详情页添加「返回」按钮,点击返回列表页;
- 给列表添加下拉刷新(
RefreshIndicator)和上拉加载更多功能。
第三阶段:核心进阶(第5-8周)------ 掌握「实战必备」技能
第5周:网络请求与 JSON 解析(对接后端数据)
目标:能用 Dio 发起网络请求,解析 JSON 数据并展示到 UI
核心知识点:
-
网络请求库:Dio
-
步骤1:添加依赖(
pubspec.yaml):yamldependencies: flutter: sdk: flutter dio: ^5.4.0 # 添加 Dio 依赖(去 pub.dev 查最新版本) -
步骤2:执行
flutter pub get安装依赖; -
步骤3:发起 GET/POST 请求:
dartimport '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"); } }
-
-
JSON 解析(模型类 + fromJson/toJson)
-
问题:直接操作 JSON 字典(
Map)容易出错,需转换成模型类; -
步骤1:定义模型类(以新闻为例):
dartclass 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, }; } }
-
-
结合 UI 展示网络数据(FutureBuilder)
用
FutureBuilder处理异步请求,展示加载中、成功、失败状态:dartclass 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」
- 用 Dio 请求真实接口(推荐:聚合数据新闻接口,需申请 API Key);
- 解析 JSON 数据,展示新闻列表(包含标题、图片、时间);
- 点击列表项跳转到新闻详情页,展示完整内容;
- 处理异常情况(无网络、接口报错),显示友好提示。
第6周:状态管理进阶(Provider)
目标:解决「跨组件传参」问题,掌握工业级状态管理方案 Provider
核心知识点:
-
为什么需要 Provider?
- 问题:当 App 复杂时,
setState只能管理单个组件的状态,跨组件(比如首页和详情页共享用户信息)传参困难; - 解决方案:用 Provider 把「共享状态」抽离出来,全局管理,任何组件都能访问和修改。
- 问题:当 App 复杂时,
-
Provider 核心概念
ChangeNotifier:状态模型类,继承它可以监听状态变化;ChangeNotifierProvider:提供器,把状态模型暴露给子组件;Consumer:消费者,从提供器中获取状态,状态变化时重新构建;Provider.of:另一种获取状态的方式(适合不需要重建的场景)。
-
Provider 实战:共享用户登录状态
步骤1:添加依赖(
pubspec.yaml):yamldependencies: provider: ^6.1.1 # Provider 依赖步骤2:定义状态模型类(
UserModel):dartimport '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:在根组件中提供状态:
dartclass 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:在登录页修改状态:
dartclass 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:在首页获取状态:
dartclass 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
- 用 Provider 管理「新闻列表数据」,实现列表页和详情页共享数据;
- 新增「收藏新闻」功能,收藏状态用 Provider 管理,跨页面同步;
- 实现「夜间模式切换」,主题状态用 Provider 管理,全局生效。
第7周:本地存储(SharedPreferences + SQLite)
目标:掌握轻量级存储和数据库存储,实现数据持久化
核心知识点:
-
轻量级存储:SharedPreferences
-
适用场景:存储少量简单数据(如用户名、开关状态、token);
-
步骤1:添加依赖:
yamldependencies: shared_preferences: ^2.2.2 -
步骤2:使用示例(存储/读取用户名):
dartimport '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"); }
-
-
数据库存储:SQLite(sqflite)
-
适用场景:存储大量结构化数据(如收藏的新闻、联系人列表);
-
步骤1:添加依赖:
yamldependencies: sqflite: ^2.3.0 path: ^1.8.3 # 用于拼接数据库路径 -
步骤2:使用示例(创建新闻收藏表,增删改查):
dartimport '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的本地存储功能
- 用 SharedPreferences 存储「登录状态」和「夜间模式开关状态」,App 重启后保持;
- 用 SQLite 实现「新闻收藏」功能:点击收藏按钮保存新闻到数据库,在「我的收藏」页面展示,支持取消收藏;
- 实现「离线查看收藏新闻」功能,无网络时也能查看。
第8周:常用插件与原生交互
目标:掌握 Flutter 常用插件的使用,了解 Flutter 与原生(Android/iOS)的交互方式
核心知识点:
-
常用插件推荐(直接用 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, ); -
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添加插件功能
- 添加「分享新闻」功能(用 share_plus),支持分享新闻标题和链接;
- 添加「头像上传」功能(用 image_picker),选择本地图片或拍照,显示预览;
- 添加「拨打电话」功能(用 url_launcher),在「关于我们」页面添加客服电话,点击拨打。
第四阶段:项目实战(第9-10周)------ 独立开发完整App
项目主题:TodoList 待办清单App(适合新手,覆盖所有核心技能)
项目需求:
-
功能模块:
- 登录/注册(用 SharedPreferences 存储用户信息);
- 待办列表(添加、编辑、删除、标记完成/未完成);
- 待办分类(工作、学习、生活,用 SQLite 存储);
- 数据统计(完成率、今日待办数,用 Provider 管理状态);
- 设置页面(夜间模式、清除缓存、关于我们)。
-
技术栈:
- 状态管理:Provider;
- 本地存储:SharedPreferences(用户信息)+ SQLite(待办数据);
- 路由:命名路由;
- 插件:fluttertoast(提示)、share_plus(分享待办)。
开发步骤(第9周:搭建架构 + 核心功能;第10周:完善功能 + 调试优化):
-
第9周 Day1-2:项目架构设计
-
创建项目结构:
lib/ ├── main.dart # 入口文件 ├── models/ # 模型类(User、Todo、Category) ├── providers/ # Provider 状态管理(UserProvider、TodoProvider) ├── services/ # 服务类(数据库、本地存储、网络) ├── screens/ # 页面(登录、注册、首页、待办详情、设置) ├── widgets/ # 自定义组件(待办项卡片、分类标签) └── utils/ # 工具类(常量、工具函数) -
配置路由、主题、Provider 全局状态。
-
-
第9周 Day3-5:核心功能开发
- 登录/注册页面(表单验证 + 本地存储用户信息);
- 待办列表页面(SQLite 增删改查 + Provider 状态更新);
- 待办添加/编辑页面(表单 + 分类选择)。
-
第10周 Day1-3:完善功能
- 数据统计页面(计算完成率、今日待办数);
- 设置页面(夜间模式切换、清除缓存);
- 添加插件功能(分享待办、提示框)。
-
第10周 Day4-7:调试优化
- 修复 Bug(如数据同步、状态刷新问题);
- UI 优化(适配不同屏幕、美化组件);
- 性能优化(减少不必要的重建、数据库索引优化)。
项目交付:
- 可运行的 App 测试包(Android APK / iOS IPA);
- 完整的源代码(含注释);
- 简单的开发文档(记录核心功能实现思路)。
第五阶段:底层原理(第11周)------ 知其然也知其所以然
核心目标:理解 Flutter 底层逻辑,能排查常见问题
核心知识点:
-
Flutter 渲染原理(三棵树)
- 小白解释:Flutter 渲染 UI 时,会创建三棵树,层层递进将 Widget 转换成屏幕上的像素;
- 三棵树关系:
Widget 树:描述 UI 结构(不可变,如 Text、Container);Element 树:Widget 的实例(可变,负责管理 Widget 的生命周期,如创建、更新、销毁);RenderObject 树:负责布局和绘制(计算组件位置、大小,绘制像素)。
- 渲染流程:Widget 树 → Element 树 → RenderObject 树 → 屏幕渲染。
-
热重载原理
- 核心:Flutter 会保留当前 App 的状态(Element 树和 RenderObject 树),只重新构建修改后的 Widget 树,然后更新 Element 树和 RenderObject 树,无需重启 App,所以速度快;
- 适用场景:修改 UI 结构、样式、逻辑代码(不修改状态类、路由配置等);
- 不生效场景:修改
main函数、状态类的构造函数、全局常量等,需热重启。
-
Widget 生命周期
-
无状态组件(StatelessWidget):只有
build方法,每次重建都会重新调用; -
有状态组件(StatefulWidget):State 类的生命周期:
createState() → initState()(初始化状态)→ didChangeDependencies()(依赖变化)→ build()(构建UI)→ didUpdateWidget()(Widget 重新构建)→ dispose()(组件销毁,释放资源)
-
-
常见问题排查思路
- UI 不更新:检查是否调用
setState(有状态组件)或notifyListeners(Provider); - 布局错乱:检查父组件的约束(如 Container 的 width/height、布局组件的对齐方式);
- 性能卡顿:避免在
build方法中创建对象(如TextStyle应抽成常量)、减少不必要的重建(用const构造函数、Consumer局部刷新); - 网络请求失败:检查接口地址、参数、权限(如 iOS 需开启网络权限)。
- UI 不更新:检查是否调用
本周任务:
- 阅读 Flutter 官方文档的「底层原理」部分:Flutter 架构概览;
- 分析自己的 TodoList 项目,梳理核心组件的生命周期;
- 故意在项目中引入一个 Bug(如 UI 不更新、布局错乱),用底层原理知识排查并修复。
第六阶段:综合提升(第12周)------ 进阶与发布
核心目标:掌握性能优化、打包发布,了解 Flutter 进阶特性
核心知识点:
-
性能优化技巧
- 减少 Widget 重建:
- 用
const构造函数(如const Text("固定文本")); - 抽离不变的 Widget 为单独组件;
- Provider 中用
Consumer局部刷新,而非Provider.of全局刷新。
- 用
- 列表性能优化:
- 用
ListView.builder(懒加载,只构建可见项); - 用
itemExtent指定列表项高度,减少布局计算; - 复杂列表项用
RepaintBoundary隔离重绘区域。
- 用
- 图片优化:
- 压缩图片尺寸(避免加载过大图片);
- 用
CachedNetworkImage缓存网络图片(减少重复请求)。
- 减少 Widget 重建:
-
打包发布
- Android 打包(生成 APK/AAB):
- 生成签名文件(key.jks):用 Android Studio 或命令行生成;
- 配置
android/app/build.gradle:添加签名信息; - 执行打包命令:
flutter build appbundle(AAB,Google Play 推荐)或flutter build apk(APK)。
- iOS 打包(生成 IPA):
- 配置 Xcode 项目(Bundle ID、签名证书);
- 选择模拟器/真机,执行
flutter build ios; - 用 Xcode 归档(Archive)并上传到 App Store Connect。
- Android 打包(生成 APK/AAB):
-
Flutter 进阶特性(可选)
- 自定义 Widget:继承
StatelessWidget/StatefulWidget,重写build方法; - 动画:用
AnimatedContainer、AnimationController实现简单动画; - Flutter 3.x+ 新特性:
Material 3主题、GoRouter(路由管理新方案)、Riverpod(Provider 升级版)。
- 自定义 Widget:继承
本周任务:
- 优化 TodoList 项目性能(至少实现 2 个优化点);
- 打包生成 Android APK 和 iOS IPA 测试包;
- 尝试接入
Material 3主题,美化 App 界面; - 总结学习心得,梳理知识体系。
小白学习小贴士
- 多动手,少死记:Flutter 是实战型技术,每个知识点都要跟着敲代码,跑通示例,不要只看不动手;
- 遇到问题先自查:终端报错信息 → 官方文档 → 搜索引擎(优先 Stack Overflow、掘金);
- 循序渐进,不急于求成:底层原理可以先放一放,先掌握基础和实战技能,有了项目经验再回头看原理,更容易理解;
- 关注社区动态:Flutter 更新快,关注 Flutter 官方博客、掘金 Flutter 专栏,了解最新特性和最佳实践。
按照这个计划学习,12周后你将从「小白」成长为「能独立开发并发布 Flutter App 的开发者」,后续可以根据兴趣深入学习某一方向(如 Flutter 桌面端、Web 端、游戏开发等)!如果学习过程中遇到具体问题,随时可以针对性提问~