Flutter 高频面试问答
# 📱 Flutter 高频面试问答
适合人群:准备Flutter面试的开发者、Flutter学习者、移动端开发者
问题类型:核心概念、渲染原理、状态管理、性能优化、平台交互
标签:FAQ - 深度解析Flutter核心技术点
# 📖 目录
# 🎯 核心概念
# Q1: Flutter 的三棵树架构是什么?它们之间有什么关系?
回答要点:
Flutter 采用三棵树架构来实现高效的UI渲染和更新:
- Widget 树:描述UI的配置信息,不可变
- Element 树:Widget 的实例化,管理Widget的生命周期
- RenderObject 树:负责实际的布局和绘制
三棵树的关系:
配置描述] --> B[Element Tree
实例管理] B --> C[RenderObject Tree
布局绘制] A1[Widget
不可变] --> B1[Element
可变] B1 --> C1[RenderObject
可变] A1 -.-> A2[rebuild时创建新Widget] B1 -.-> B2[复用Element实例] C1 -.-> C2[更新布局和绘制]
深度解析:
# Widget 树(配置树)
// Widget 是不可变的配置对象
class MyWidget extends StatelessWidget {
final String title;
const MyWidget({Key? key, required this.title}) : super(key: key);
Widget build(BuildContext context) {
return Container(
child: Text(title),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# Element 树(实例树)
// Element 是 Widget 的可变实例
// 它维护了 Widget 和 RenderObject 之间的连接
class MyElement extends ComponentElement {
Widget build() {
return widget.build(this);
}
void update(Widget newWidget) {
// 更新逻辑:比较新旧Widget,决定是否需要重建
super.update(newWidget);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# RenderObject 树(渲染树)
// RenderObject 负责实际的布局和绘制
class MyRenderObject extends RenderBox {
void performLayout() {
// 布局计算
size = constraints.biggest;
}
void paint(PaintingContext context, Offset offset) {
// 绘制逻辑
context.paintChild(child, offset);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
更新流程:
- Widget 重建:调用
setState()或外部状态变化 - Element 比较:Element 比较新旧 Widget 是否相同
- 选择性更新:
- 如果 Widget 相同 → 复用 Element 和 RenderObject
- 如果 Widget 不同 → 重建 Element 和 RenderObject
代码示例:
class CounterWidget extends StatefulWidget {
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _increment() {
setState(() {
_counter++; // 触发重建
});
}
Widget build(BuildContext context) {
// 每次调用都会创建新的Widget实例
return Column(
children: [
Text('Counter: $_counter'), // 新的Widget
ElevatedButton(
onPressed: _increment,
child: Text('Increment'), // 新的Widget
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Q2: Widget 为什么是不可变的?这带来了什么好处?
回答要点:
# 不可变性的原理
Widget 不可变意味着:
- 创建后不能修改其属性
- 每次状态变化都创建新的 Widget 实例
- 通过比较 Widget 的
runtimeType和key来判断是否需要更新
// Widget 的不可变性
class MyWidget extends StatelessWidget {
final String title;
final int count;
// 构造函数参数必须是 final
const MyWidget({
Key? key,
required this.title,
required this.count,
}) : super(key: key);
// 没有 setter 方法,属性不可修改
// void set title(String newTitle) {} // 错误!不可修改
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 带来的好处
1. 简化状态管理
// 不可变使得状态变化变得可预测
class UserProfile extends StatelessWidget {
final User user;
const UserProfile({Key? key, required this.user}) : super(key: key);
Widget build(BuildContext context) {
// user 对象不可变,不会意外修改
return Column(
children: [
Text(user.name),
Text(user.email),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. 高效的更新机制
// Flutter 可以通过简单比较来判断是否需要重建
bool shouldUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType != newWidget.runtimeType ||
oldWidget.key != newWidget.key;
}
2
3
4
5
3. 线程安全
// 不可变对象天然线程安全
class CounterState {
final int count;
const CounterState(this.count);
// 创建新状态而不是修改现有状态
CounterState increment() {
return CounterState(count + 1);
}
}
2
3
4
5
6
7
8
9
10
11
4. 更好的测试性
// 不可变对象更容易测试
void testWidgetCreation() {
final widget = MyWidget(title: 'Test', count: 5);
// 可以安全地传递和比较
expect(widget.title, equals('Test'));
expect(widget.count, equals(5));
// 创建新实例而不是修改
final newWidget = MyWidget(title: 'Updated', count: 6);
expect(newWidget.title, equals('Updated'));
}
2
3
4
5
6
7
8
9
10
11
12
# 性能优化技巧
使用 const 构造函数
// ✅ 好的做法:使用 const 构造函数
class OptimizedWidget extends StatelessWidget {
const OptimizedWidget({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return const Text('Static content'); // const Widget
}
}
// ❌ 避免:每次重建都创建新实例
class NonOptimizedWidget extends StatelessWidget {
NonOptimizedWidget({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Text('Static content'); // 每次都是新实例
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Q3: BuildContext 的作用是什么?它在渲染树中的位置如何确定?
回答要点:
# BuildContext 的本质
BuildContext 实际上就是 Element 对象,它代表了 Widget 在 Element 树中的位置。
// BuildContext 的真实身份
abstract class BuildContext {
// BuildContext 就是 Element
}
abstract class Element extends BuildContext {
// Element 实现了 BuildContext 接口
}
2
3
4
5
6
7
8
# BuildContext 的作用
1. 定位 Widget 在树中的位置
class MyWidget extends StatelessWidget {
Widget build(BuildContext context) {
// context 就是当前 Widget 对应的 Element
print('MyWidget 在树中的位置: $context');
return Container(
child: ChildWidget(), // 子 Widget 会获得新的 context
);
}
}
2
3
4
5
6
7
8
9
10
11
2. 访问祖先 Widget 的数据
class ThemeWidget extends InheritedWidget {
final Color primaryColor;
const ThemeWidget({
Key? key,
required this.primaryColor,
required Widget child,
}) : super(key: key, child: child);
// 通过 context 查找祖先 ThemeWidget
static ThemeWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ThemeWidget>();
}
}
class ChildWidget extends StatelessWidget {
Widget build(BuildContext context) {
// 通过 context 访问祖先的 ThemeWidget
final theme = ThemeWidget.of(context);
return Container(
color: theme?.primaryColor,
child: Text('Themed content'),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3. 导航和路由
class NavigationExample extends StatelessWidget {
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// 使用 context 进行导航
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
child: Text('Navigate'),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# Context 的层级关系
class ContextHierarchyExample extends StatelessWidget {
Widget build(BuildContext context) {
// context 是当前 Widget 在树中的位置
print('Root context: $context');
return ThemeWidget(
primaryColor: Colors.blue,
child: Builder(
builder: (childContext) {
// childContext 是 Builder Widget 的位置
print('Builder context: $childContext');
return Container(
child: ChildWidget(), // 会获得新的 context
);
},
),
);
}
}
class ChildWidget extends StatelessWidget {
Widget build(BuildContext context) {
// context 是 ChildWidget 在树中的位置
print('Child context: $context');
// 可以访问祖先的 ThemeWidget
final theme = ThemeWidget.of(context);
return Text('Child widget');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 常见错误和注意事项
1. 在 build 方法外使用 context
class BadExample extends StatefulWidget {
_BadExampleState createState() => _BadExampleState();
}
class _BadExampleState extends State<BadExample> {
void initState() {
super.initState();
// ❌ 错误:在 initState 中使用 context
// Navigator.of(context).push(...); // 可能为 null
}
void didChangeDependencies() {
super.didChangeDependencies();
// ✅ 正确:在 didChangeDependencies 中使用
final theme = Theme.of(context);
}
void _handleButtonPress() {
// ✅ 正确:在事件处理中使用
Navigator.of(context).push(...);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2. 使用错误的 context
class ContextMistakeExample extends StatelessWidget {
Widget build(BuildContext context) {
return FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
// ❌ 错误:这里的 context 是 FutureBuilder 的 context
// 可能无法访问到期望的祖先 Widget
return ElevatedButton(
onPressed: () {
// 可能无法找到正确的 Navigator
Navigator.of(context).pop();
},
child: Text('Go back'),
);
},
);
}
}
class FixedExample extends StatelessWidget {
Widget build(BuildContext context) {
return FutureBuilder(
future: fetchData(),
builder: (builderContext, snapshot) {
return ElevatedButton(
onPressed: () {
// ✅ 正确:使用外层的 context
Navigator.of(context).pop();
},
child: Text('Go back'),
);
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 🔄 生命周期管理
# Q4: StatefulWidget 和 StatelessWidget 有什么区别?什么时候使用哪个?
回答要点:
# 基本区别
| 特性 | StatelessWidget | StatefulWidget |
|---|---|---|
| 状态管理 | 无状态,不可变 | 有状态,可变 |
| 重建触发 | 父 Widget 重建时 | setState() 或父 Widget 重建时 |
| 性能 | 更轻量,重建开销小 | 较重,需要维护状态 |
| 使用场景 | 纯展示组件 | 需要交互的组件 |
# StatelessWidget 详解
class StatelessExample extends StatelessWidget {
final String title;
final int count;
// const 构造函数,提高性能
const StatelessExample({
Key? key,
required this.title,
required this.count,
}) : super(key: key);
Widget build(BuildContext context) {
// 每次父 Widget 重建时,这个方法都会被调用
// 但 Widget 本身是无状态的
return Column(
children: [
Text(title),
Text('Count: $count'),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
使用场景:
- 纯展示组件(文本、图片、图标)
- 静态布局组件
- 不需要用户交互的组件
# StatefulWidget 详解
class StatefulExample extends StatefulWidget {
final String initialTitle;
const StatefulExample({
Key? key,
required this.initialTitle,
}) : super(key: key);
_StatefulExampleState createState() => _StatefulExampleState();
}
class _StatefulExampleState extends State<StatefulExample> {
// 可变状态
int _counter = 0;
String _currentTitle = '';
void initState() {
super.initState();
_currentTitle = widget.initialTitle;
print('StatefulExample initialized');
}
void _incrementCounter() {
setState(() {
_counter++;
_currentTitle = 'Updated: $_counter';
});
}
Widget build(BuildContext context) {
return Column(
children: [
Text(_currentTitle),
Text('Counter: $_counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('Increment'),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
使用场景:
- 需要用户交互的组件(按钮、输入框)
- 需要维护内部状态的组件
- 需要监听数据变化的组件
# Q5: Flutter 的生命周期方法有哪些?它们的执行顺序是什么?
回答要点:
# 完整的生命周期流程
# 生命周期方法详解
1. createState() - 创建状态对象
class MyStatefulWidget extends StatefulWidget {
_MyStatefulWidgetState createState() {
// 每次 Widget 被插入到树中时调用
print('createState called');
return _MyStatefulWidgetState();
}
}
2
3
4
5
6
7
8
2. initState() - 初始化状态
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late StreamSubscription _subscription;
late AnimationController _controller;
void initState() {
super.initState();
// 初始化一次的状态
print('initState: Widget 初始化完成');
// 初始化订阅、控制器等
_subscription = stream.listen(_handleData);
_controller = AnimationController(vsync: this);
// 注意:此时还不能使用 context 访问 InheritedWidget
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3. didChangeDependencies() - 依赖变化
void didChangeDependencies() {
super.didChangeDependencies();
// 当 Widget 依赖的 InheritedWidget 发生变化时调用
print('didChangeDependencies: 依赖发生变化');
// 可以安全地使用 context 访问 InheritedWidget
final theme = Theme.of(context);
final locale = Localizations.of(context);
// 通常用于更新依赖于 context 的状态
}
2
3
4
5
6
7
8
9
10
11
12
13
4. build() - 构建 Widget 树
Widget build(BuildContext context) {
// 每次需要重建 UI 时调用
print('build: 构建 Widget 树');
return Container(
child: Text('Hello World'),
);
}
2
3
4
5
6
7
8
9
5. didUpdateWidget() - Widget 更新
class ParentWidget extends StatefulWidget {
final String title;
const ParentWidget({Key? key, required this.title}) : super(key: key);
_ParentWidgetState createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
Widget build(BuildContext context) {
return ChildWidget(title: widget.title);
}
}
class ChildWidget extends StatefulWidget {
final String title;
const ChildWidget({Key? key, required this.title}) : super(key: key);
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
void didUpdateWidget(ChildWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 当父 Widget 传递新的配置时调用
print('didUpdateWidget: ${oldWidget.title} -> ${widget.title}');
// 比较新旧配置,决定是否需要更新内部状态
if (oldWidget.title != widget.title) {
// 执行相应的更新逻辑
}
}
Widget build(BuildContext context) {
return Text(widget.title);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
6. deactivate() - 停用状态
void deactivate() {
// Widget 从树中移除时调用,但可能还会重新插入
print('deactivate: Widget 被停用');
// 清理一些资源,但保留可能重新使用的资源
super.deactivate();
}
2
3
4
5
6
7
8
7. dispose() - 销毁状态
void dispose() {
// Widget 永久从树中移除时调用
print('dispose: Widget 被销毁');
// 清理所有资源
_subscription.cancel();
_controller.dispose();
super.dispose();
}
2
3
4
5
6
7
8
9
10
11
# 📊 状态管理
# Q6: Flutter 中有哪些主要的状态管理方案?它们有什么区别?
回答要点:
Flutter 提供了多种状态管理方案,每种都有其适用场景:
# 状态管理方案对比
| 方案 | 复杂度 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|---|
| setState | 低 | 简单组件内部状态 | 简单直接,官方支持 | 不适合复杂应用 |
| Provider | 中 | 中小型应用 | 轻量级,易学习 | 功能相对简单 |
| Bloc | 高 | 大型复杂应用 | 可测试性强,状态可预测 | 学习曲线陡峭 |
| Riverpod | 中-高 | 现代 Flutter 应用 | 类型安全,编译时检查 | 相对较新 |
| GetX | 中 | 快速开发 | 功能全面,语法简洁 | 性能争议 |
# 1. setState - 基础状态管理
class CounterWidget extends StatefulWidget {
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _increment() {
setState(() {
_counter++; // 触发重建
});
}
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter: $_counter'),
ElevatedButton(
onPressed: _increment,
child: Text('Increment'),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
适用场景:
- 组件内部简单状态
- 原型开发
- 学习 Flutter 基础
# 2. Provider - 轻量级状态管理
// 1. 定义数据模型
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知监听者
}
void decrement() {
_count--;
notifyListeners();
}
}
// 2. 在应用顶层提供数据
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MaterialApp(
home: CounterScreen(),
),
);
}
}
// 3. 在子组件中消费数据
class CounterScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
children: [
// 使用 Consumer 监听变化
Consumer<CounterModel>(
builder: (context, counter, child) {
return Text(
'Count: ${counter.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
// 使用 Provider.of 获取数据
ElevatedButton(
onPressed: () {
Provider.of<CounterModel>(context, listen: false).increment();
},
child: Text('Increment'),
),
],
),
),
);
}
}
// 4. 使用 Selector 精确监听
class CounterDisplay extends StatelessWidget {
Widget build(BuildContext context) {
return Selector<CounterModel, int>(
selector: (context, counter) => counter.count,
builder: (context, count, child) {
return Text('Count: $count');
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Provider 的优势:
- 简单易学,官方推荐
- 自动管理生命周期
- 支持依赖注入
- 良好的测试支持
# 3. Bloc - 复杂状态管理
// 1. 定义事件
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
// 2. 定义状态
class CounterState {
final int count;
const CounterState({required this.count});
CounterState copyWith({int? count}) {
return CounterState(count: count ?? this.count);
}
}
// 3. 实现 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState(count: 0)) {
on<IncrementEvent>(_onIncrement);
on<DecrementEvent>(_onDecrement);
}
void _onIncrement(IncrementEvent event, Emitter<CounterState> emit) {
emit(state.copyWith(count: state.count + 1));
}
void _onDecrement(DecrementEvent event, Emitter<CounterState> emit) {
emit(state.copyWith(count: state.count - 1));
}
}
// 4. 在 Widget 中使用
class CounterBlocScreen extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterBloc(),
child: CounterView(),
);
}
}
class CounterView extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Bloc Counter')),
body: Center(
child: Column(
children: [
// 使用 BlocBuilder 监听状态变化
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'Count: ${state.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
context.read<CounterBloc>().add(IncrementEvent());
},
child: Text('Increment'),
),
ElevatedButton(
onPressed: () {
context.read<CounterBloc>().add(DecrementEvent());
},
child: Text('Decrement'),
),
],
),
],
),
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
Bloc 的优势:
- 状态变化可预测和可追踪
- 优秀的测试支持
- 清晰的事件-状态映射
- 适合大型复杂应用
# 4. Riverpod - 现代状态管理
// 1. 定义 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
// 2. 实现 StateNotifier
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
// 3. 在 Widget 中使用
class CounterRiverpodScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 监听状态变化
final count = ref.watch(counterProvider);
return Scaffold(
appBar: AppBar(title: Text('Riverpod Counter')),
body: Center(
child: Column(
children: [
Text(
'Count: $count',
style: Theme.of(context).textTheme.headline4,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).increment();
},
child: Text('Increment'),
),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).decrement();
},
child: Text('Decrement'),
),
],
),
],
),
),
);
}
}
// 4. 使用 Consumer 精确监听
class CounterDisplay extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Text('Count: $count');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Riverpod 的优势:
- 编译时类型安全
- 自动依赖管理
- 更好的测试支持
- 支持代码生成
# Q7: 如何选择合适的状态管理方案?
回答要点:
# 选择标准
1. 应用复杂度
// 简单应用 - 使用 setState
class SimpleCounter extends StatefulWidget {
_SimpleCounterState createState() => _SimpleCounterState();
}
class _SimpleCounterState extends State<SimpleCounter> {
int _count = 0;
Widget build(BuildContext context) {
return Column(
children: [
Text('$_count'),
ElevatedButton(
onPressed: () => setState(() => _count++),
child: Text('Increment'),
),
],
);
}
}
// 中等复杂度 - 使用 Provider
class MediumApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
ChangeNotifierProvider(create: (_) => ThemeModel()),
ChangeNotifierProvider(create: (_) => SettingsModel()),
],
child: MyApp(),
);
}
}
// 复杂应用 - 使用 Bloc
class ComplexApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => AuthBloc()),
BlocProvider(create: (_) => UserBloc()),
BlocProvider(create: (_) => ProductBloc()),
BlocProvider(create: (_) => OrderBloc()),
],
child: MyApp(),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2. 团队经验
- 初学者团队:Provider + setState
- 有经验团队:Bloc 或 Riverpod
- 大型团队:Bloc(更好的代码组织)
3. 性能要求
// 高性能要求 - 使用 Riverpod 的精确监听
class PerformanceOptimizedWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// 只监听特定字段的变化
final userName = ref.watch(userProvider.select((user) => user.name));
return Text(userName); // 只有 name 变化时才重建
}
}
// 或者使用 Bloc 的 BlocSelector
class BlocOptimizedWidget extends StatelessWidget {
Widget build(BuildContext context) {
return BlocSelector<UserBloc, UserState, String>(
selector: (state) => state.user.name,
builder: (context, name) {
return Text(name); // 只有 name 变化时才重建
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4. 测试要求
// Bloc 测试示例
void main() {
group('CounterBloc', () {
late CounterBloc counterBloc;
setUp(() {
counterBloc = CounterBloc();
});
tearDown(() {
counterBloc.close();
});
test('initial state is 0', () {
expect(counterBloc.state, const CounterState(count: 0));
});
test('increment increases count by 1', () {
// Arrange
const expected = CounterState(count: 1);
// Act
counterBloc.add(IncrementEvent());
// Assert
expectLater(counterBloc.stream, emits(expected));
});
});
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 推荐方案
小型项目(< 10 个页面)
// 使用 Provider + setState
class SmallApp extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AppState(),
child: MaterialApp(
home: HomePage(),
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
中型项目(10-50 个页面)
// 使用 Provider 或 Riverpod
class MediumApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => DataProvider()),
ChangeNotifierProvider(create: (_) => ThemeProvider()),
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
大型项目(> 50 个页面)
// 使用 Bloc 或 Riverpod
class LargeApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => AuthBloc()),
BlocProvider(create: (_) => UserBloc()),
BlocProvider(create: (_) => ProductBloc()),
BlocProvider(create: (_) => OrderBloc()),
BlocProvider(create: (_) => NotificationBloc()),
],
child: MaterialApp(
home: HomePage(),
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Q8: 如何避免不必要的 Widget 重建?
回答要点:
# 常见重建问题
1. 不必要的 Provider 监听
// ❌ 错误:监听整个对象
class BadExample extends StatelessWidget {
Widget build(BuildContext context) {
final user = Provider.of<User>(context); // 监听整个 User 对象
return Text(user.name); // 但只使用 name 字段
}
}
// ✅ 正确:只监听需要的字段
class GoodExample extends StatelessWidget {
Widget build(BuildContext context) {
final userName = context.select<User, String>((user) => user.name);
return Text(userName); // 只有 name 变化时才重建
}
}
// 或者使用 Consumer
class ConsumerExample extends StatelessWidget {
Widget build(BuildContext context) {
return Consumer<User>(
builder: (context, user, child) {
return Text(user.name);
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2. 使用 const 构造函数
// ❌ 错误:每次都创建新实例
class BadWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
Text('Static text'), // 每次都创建新实例
Container(
child: Text('Another static text'),
),
],
);
}
}
// ✅ 正确:使用 const 构造函数
class GoodWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: const [
Text('Static text'), // const 实例,不会重建
Container(
child: Text('Another static text'),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
3. 使用 RepaintBoundary
class OptimizedWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
// 复杂的动画组件,独立重绘
RepaintBoundary(
child: ComplexAnimationWidget(),
),
// 简单的文本组件
Text('Simple text'),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. 使用 AutomaticKeepAliveClientMixin
class KeepAlivePage extends StatefulWidget {
_KeepAlivePageState createState() => _KeepAlivePageState();
}
class _KeepAlivePageState extends State<KeepAlivePage>
with AutomaticKeepAliveClientMixin {
bool get wantKeepAlive => true; // 保持状态
Widget build(BuildContext context) {
super.build(context); // 必须调用
return ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
5. 使用 ValueKey 优化列表
class OptimizedList extends StatelessWidget {
final List<String> items;
const OptimizedList({Key? key, required this.items}) : super(key: key);
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
// 使用 ValueKey 帮助 Flutter 识别列表项
return ListTile(
key: ValueKey(item), // 或者使用 item.hashCode
title: Text(item),
);
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
6. 使用 Selector 精确监听
// Provider 中的 Selector
class UserNameDisplay extends StatelessWidget {
Widget build(BuildContext context) {
return Selector<User, String>(
selector: (context, user) => user.name,
builder: (context, name, child) {
return Text(name); // 只有 name 变化时才重建
},
);
}
}
// Riverpod 中的 select
class RiverpodSelector extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final userName = ref.watch(userProvider.select((user) => user.name));
return Text(userName);
}
}
// Bloc 中的 BlocSelector
class BlocSelectorExample extends StatelessWidget {
Widget build(BuildContext context) {
return BlocSelector<UserBloc, UserState, String>(
selector: (state) => state.user.name,
builder: (context, name) {
return Text(name);
},
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 性能监控
class PerformanceMonitor extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
builder: (context, child) {
// 监控重建次数
return RepaintBoundary(
child: child!,
);
},
);
}
}
// 使用 Flutter Inspector 检查重建
class DebugWidget extends StatelessWidget {
Widget build(BuildContext context) {
// 在 Debug 模式下打印重建信息
if (kDebugMode) {
print('DebugWidget rebuilt at ${DateTime.now()}');
}
return Container(
child: Text('Debug Widget'),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 🎨 渲染原理
# Q9: Flutter 的渲染流程是怎样的?布局、绘制、合成分别是什么?
回答要点:
Flutter 的渲染流程分为三个主要阶段:布局(Layout)、绘制(Paint)、合成(Composite)。
# 渲染流程概览
# 1. 布局阶段(Layout)
目的: 确定每个 Widget 在屏幕上的位置和大小
// 自定义 RenderObject 示例
class CustomRenderBox extends RenderBox {
void performLayout() {
// 1. 获取父级约束
final BoxConstraints constraints = this.constraints;
// 2. 计算子元素的大小
if (child != null) {
child!.layout(constraints, parentUsesSize: true);
}
// 3. 确定自身大小
size = constraints.biggest; // 或者使用 constraints.constrain(desiredSize)
}
void paint(PaintingContext context, Offset offset) {
// 绘制逻辑
}
}
// 布局约束示例
class LayoutExample extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: 200, // 明确指定宽度
height: 100, // 明确指定高度
child: Column(
children: [
Container(
height: 50, // 固定高度
child: Text('Fixed height'),
),
Expanded( // 占用剩余空间
child: Text('Expanded content'),
),
],
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
布局约束类型:
- BoxConstraints:矩形约束(最小/最大宽高)
- SliverConstraints:滚动约束
- RenderFlex:弹性布局(Row、Column)
# 2. 绘制阶段(Paint)
目的: 将布局好的元素绘制成像素
// 自定义绘制示例
class CustomPaintExample extends CustomPainter {
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
// 绘制矩形
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height),
paint,
);
// 绘制圆形
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 4,
paint..color = Colors.red,
);
// 绘制文本
final textPainter = TextPainter(
text: TextSpan(
text: 'Custom Paint',
style: TextStyle(color: Colors.white, fontSize: 24),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
(size.width - textPainter.width) / 2,
(size.height - textPainter.height) / 2,
),
);
}
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
// 使用 CustomPaint Widget
class CustomPaintWidget extends StatelessWidget {
Widget build(BuildContext context) {
return CustomPaint(
painter: CustomPaintExample(),
size: Size(200, 200),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
绘制相关类:
- Canvas:绘制画布
- Paint:画笔样式
- Path:绘制路径
- TextPainter:文本绘制
# 3. 合成阶段(Composite)
目的: 将多个图层合成为最终图像
// 图层合成示例
class CompositeExample extends StatelessWidget {
Widget build(BuildContext context) {
return Stack(
children: [
// 背景层
Container(
width: 200,
height: 200,
color: Colors.blue,
),
// 中间层 - 带透明度
Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.5),
borderRadius: BorderRadius.circular(20),
),
),
// 前景层 - 带阴影
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(50),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 10,
offset: Offset(5, 5),
),
],
),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 渲染优化技巧
1. 使用 RepaintBoundary 隔离重绘
class OptimizedAnimation extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
Text('Static content'),
// 动画组件独立重绘
RepaintBoundary(
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: 100,
height: 100,
color: Colors.blue,
),
),
Text('More static content'),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2. 使用 CustomClipper 优化裁剪
class CustomClipperExample extends StatelessWidget {
Widget build(BuildContext context) {
return ClipPath(
clipper: MyCustomClipper(),
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Center(
child: Text(
'Clipped',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
);
}
}
class MyCustomClipper extends CustomClipper<Path> {
Path getClip(Size size) {
final path = Path();
path.moveTo(0, 0);
path.lineTo(size.width, 0);
path.lineTo(size.width * 0.8, size.height);
path.lineTo(0, size.height);
path.close();
return path;
}
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Q10: Flutter 中的布局系统是如何工作的?
回答要点:
Flutter 采用基于约束的布局系统,遵循"父级约束,子级决定"的原则。
# 布局约束系统
min/max width/height] C --> C1[子级决定最终大小] E --> E1[Offset 定位]
# 1. 约束传递机制
class ConstraintExample extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
// 父级约束:宽度 100-200,高度 50-100
constraints: BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 50,
maxHeight: 100,
),
child: Container(
// 子级决定:宽度 150,高度 75
width: 150,
height: 75,
color: Colors.blue,
child: Text('Constrained'),
),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2. 常用布局 Widget
Flex 布局(Row、Column)
class FlexLayoutExample extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center, // 主轴对齐
crossAxisAlignment: CrossAxisAlignment.start, // 交叉轴对齐
mainAxisSize: MainAxisSize.min, // 主轴大小
children: [
Container(
width: 100,
height: 50,
color: Colors.red,
child: Text('Item 1'),
),
// 弹性布局
Expanded(
flex: 2, // 占用 2 份空间
child: Container(
color: Colors.green,
child: Text('Expanded 1'),
),
),
Expanded(
flex: 1, // 占用 1 份空间
child: Container(
color: Colors.blue,
child: Text('Expanded 2'),
),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Stack 布局
class StackLayoutExample extends StatelessWidget {
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center, // 默认对齐方式
clipBehavior: Clip.hardEdge, // 裁剪行为
children: [
// 背景
Container(
width: 200,
height: 200,
color: Colors.grey,
),
// 绝对定位
Positioned(
top: 20,
left: 20,
child: Container(
width: 50,
height: 50,
color: Colors.red,
),
),
// 相对定位
Positioned(
bottom: 20,
right: 20,
child: Container(
width: 50,
height: 50,
color: Colors.blue,
),
),
// 填满整个 Stack
Positioned.fill(
child: Container(
color: Colors.yellow.withOpacity(0.3),
child: Center(
child: Text('Fill'),
),
),
),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Wrap 布局
class WrapLayoutExample extends StatelessWidget {
Widget build(BuildContext context) {
return Wrap(
direction: Axis.horizontal, // 方向
alignment: WrapAlignment.center, // 对齐方式
spacing: 8.0, // 主轴间距
runSpacing: 8.0, // 交叉轴间距
children: List.generate(10, (index) {
return Container(
width: 80,
height: 40,
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Text('$index'),
),
);
}),
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. 自定义布局
// 自定义布局 Widget
class CustomLayout extends StatelessWidget {
final List<Widget> children;
const CustomLayout({Key? key, required this.children}) : super(key: key);
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: MyLayoutDelegate(),
children: children,
);
}
}
class MyLayoutDelegate extends MultiChildLayoutDelegate {
void performLayout(Size size) {
// 布局所有子元素
for (int i = 0; i < children.length; i++) {
final child = children.elementAt(i);
if (child != null) {
// 计算子元素约束
final constraints = BoxConstraints(
minWidth: 0,
maxWidth: size.width,
minHeight: 0,
maxHeight: size.height,
);
// 布局子元素
final childSize = layoutChild(child, constraints);
// 定位子元素
positionChild(
child,
Offset(
i * (childSize.width + 10), // 水平排列,间距 10
(size.height - childSize.height) / 2, // 垂直居中
),
);
}
}
}
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 4. 响应式布局
class ResponsiveLayout extends StatelessWidget {
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
// 根据屏幕宽度决定布局
if (constraints.maxWidth > 600) {
// 平板/桌面布局
return TabletLayout();
} else if (constraints.maxWidth > 400) {
// 大手机布局
return LargePhoneLayout();
} else {
// 小手机布局
return SmallPhoneLayout();
}
},
);
}
}
class TabletLayout extends StatelessWidget {
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
flex: 1,
child: Sidebar(),
),
Expanded(
flex: 2,
child: MainContent(),
),
],
);
}
}
class LargePhoneLayout extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
AppBar(),
Expanded(child: MainContent()),
BottomNavigation(),
],
);
}
}
class SmallPhoneLayout extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
AppBar(),
Expanded(child: MainContent()),
// 更紧凑的底部导航
CompactBottomNavigation(),
],
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# Q11: Flutter 中的绘制系统是如何工作的?
回答要点:
Flutter 的绘制系统基于 Skia 图形引擎,使用 Canvas API 进行绘制。
# 绘制流程
# 1. Canvas 绘制基础
class CanvasPaintingExample extends CustomPainter {
void paint(Canvas canvas, Size size) {
// 1. 绘制基础形状
_drawBasicShapes(canvas, size);
// 2. 绘制路径
_drawPaths(canvas, size);
// 3. 绘制文本
_drawText(canvas, size);
// 4. 应用变换
_applyTransformations(canvas, size);
}
void _drawBasicShapes(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
// 绘制矩形
canvas.drawRect(
Rect.fromLTWH(10, 10, 100, 50),
paint,
);
// 绘制圆形
canvas.drawCircle(
Offset(60, 100),
30,
paint..color = Colors.red,
);
// 绘制椭圆
canvas.drawOval(
Rect.fromLTWH(10, 150, 100, 50),
paint..color = Colors.green,
);
// 绘制线条
canvas.drawLine(
Offset(10, 220),
Offset(110, 220),
paint..color = Colors.orange..strokeWidth = 5,
);
}
void _drawPaths(Canvas canvas, Size size) {
final path = Path();
path.moveTo(50, 250);
path.lineTo(100, 250);
path.quadraticBezierTo(125, 275, 100, 300);
path.lineTo(50, 300);
path.close();
canvas.drawPath(
path,
Paint()..color = Colors.purple,
);
}
void _drawText(Canvas canvas, Size size) {
final textPainter = TextPainter(
text: TextSpan(
text: 'Canvas Text',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(10, 320),
);
}
void _applyTransformations(Canvas canvas, Size size) {
// 保存当前状态
canvas.save();
// 应用变换
canvas.translate(200, 100);
canvas.rotate(0.5); // 旋转
// 绘制变换后的图形
canvas.drawRect(
Rect.fromLTWH(0, 0, 50, 50),
Paint()..color = Colors.amber,
);
// 恢复状态
canvas.restore();
}
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 2. 高级绘制技巧
渐变绘制
class GradientPaintingExample extends CustomPainter {
void paint(Canvas canvas, Size size) {
// 线性渐变
final linearGradient = LinearGradient(
colors: [Colors.red, Colors.blue],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
final linearPaint = Paint()
..shader = linearGradient.createShader(Rect.fromLTWH(0, 0, size.width, size.height / 2));
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width, size.height / 2),
linearPaint,
);
// 径向渐变
final radialGradient = RadialGradient(
colors: [Colors.yellow, Colors.green],
center: Alignment.center,
radius: 0.8,
);
final radialPaint = Paint()
..shader = radialGradient.createShader(Rect.fromLTWH(0, size.height / 2, size.width, size.height / 2));
canvas.drawRect(
Rect.fromLTWH(0, size.height / 2, size.width, size.height / 2),
radialPaint,
);
}
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
阴影和滤镜效果
class ShadowPaintingExample extends CustomPainter {
void paint(Canvas canvas, Size size) {
// 阴影效果
final shadowPaint = Paint()
..color = Colors.grey.withOpacity(0.3)
..maskFilter = MaskFilter.blur(BlurStyle.normal, 5);
// 绘制阴影
canvas.drawRect(
Rect.fromLTWH(15, 15, 100, 50),
shadowPaint,
);
// 绘制主体
canvas.drawRect(
Rect.fromLTWH(10, 10, 100, 50),
Paint()..color = Colors.blue,
);
// 滤镜效果
final filterPaint = Paint()
..color = Colors.red
..imageFilter = ImageFilter.blur(sigmaX: 2, sigmaY: 2);
canvas.drawRect(
Rect.fromLTWH(130, 10, 100, 50),
filterPaint,
);
}
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 3. 性能优化
使用 RepaintBoundary 优化重绘
class OptimizedPaintingExample extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
Text('Static content'),
// 独立的重绘边界
RepaintBoundary(
child: CustomPaint(
painter: AnimatedPainter(),
size: Size(200, 200),
),
),
Text('More static content'),
],
);
}
}
class AnimatedPainter extends CustomPainter {
void paint(Canvas canvas, Size size) {
// 动画绘制逻辑
final paint = Paint()..color = Colors.blue;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 4,
paint,
);
}
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
缓存绘制结果
class CachedPaintingExample extends CustomPainter {
static ui.Image? _cachedImage;
void paint(Canvas canvas, Size size) {
if (_cachedImage == null) {
// 创建缓存图像
_createCachedImage(size);
}
if (_cachedImage != null) {
// 使用缓存的图像
canvas.drawImage(_cachedImage!, Offset.zero, Paint());
}
}
void _createCachedImage(Size size) async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
// 复杂的绘制操作
_drawComplexShape(canvas, size);
final picture = recorder.endRecording();
_cachedImage = await picture.toImage(size.width.toInt(), size.height.toInt());
}
void _drawComplexShape(Canvas canvas, Size size) {
// 复杂的绘制逻辑
final paint = Paint()..color = Colors.blue;
for (int i = 0; i < 100; i++) {
canvas.drawCircle(
Offset(
(i % 10) * (size.width / 10),
(i ~/ 10) * (size.height / 10),
),
5,
paint,
);
}
}
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46