避免不必要重建的策略
- 使用
const
构造函数:如果StatefulWidget
及其子树在状态变化时保持不变,可以将其定义为const
构造函数,这样Flutter在构建时可以复用相同的实例,减少不必要的重建。例如:
class MyConstWidget extends StatelessWidget {
const MyConstWidget({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
- 局部重建:使用
AnimatedBuilder
、IndexedStack
、Offstage
等组件实现局部重建。
- AnimatedBuilder:适用于基于动画值进行局部更新。如在一个包含动画的组件中,只有动画相关部分需要更新,就可以使用
AnimatedBuilder
。
class MyAnimatedWidget extends StatefulWidget {
const MyAnimatedWidget({super.key});
@override
State<MyAnimatedWidget> createState() => _MyAnimatedWidgetState();
}
class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 1).animate(_controller);
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.scale(
scale: _animation.value,
child: child,
);
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
}
}
- IndexedStack:当需要在多个子组件中切换显示,且每次只显示一个子组件时,
IndexedStack
可以避免其他子组件不必要的重建。
class IndexedStackExample extends StatefulWidget {
const IndexedStackExample({super.key});
@override
State<IndexedStackExample> createState() => _IndexedStackExampleState();
}
class _IndexedStackExampleState extends State<IndexedStackExample> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
IndexedStack(
index: _currentIndex,
children: [
Container(
width: 100,
height: 100,
color: Colors.red,
),
Container(
width: 100,
height: 100,
color: Colors.green,
)
],
),
Row(
children: [
ElevatedButton(
onPressed: () {
setState(() {
_currentIndex = 0;
});
},
child: const Text('Show Red'),
),
ElevatedButton(
onPressed: () {
setState(() {
_currentIndex = 1;
});
},
child: const Text('Show Green'),
)
],
)
],
);
}
}
- Offstage:用于根据条件隐藏或显示组件,隐藏时不会重建。
class OffstageExample extends StatefulWidget {
const OffstageExample({super.key});
@override
State<OffstageExample> createState() => _OffstageExampleState();
}
class _OffstageExampleState extends State<OffstageExample> {
bool _isVisible = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
Offstage(
offstage:!_isVisible,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
ElevatedButton(
onPressed: () {
setState(() {
_isVisible =!_isVisible;
});
},
child: const Text('Toggle Visibility'),
)
],
);
}
}
- 状态提升:将状态提升到父组件,使多个子组件可以共享状态,减少重复的状态管理和重建。例如,有两个子组件
Child1
和Child2
都依赖于某个状态值,将这个状态提升到父组件,通过InheritedWidget
或状态管理库(如Provider)传递给子组件。
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Child1(counter: _counter),
Child2(counter: _counter, incrementCounter: _incrementCounter),
],
);
}
}
class Child1 extends StatelessWidget {
final int counter;
const Child1({super.key, required this.counter});
@override
Widget build(BuildContext context) {
return Text('Counter value in Child1: $counter');
}
}
class Child2 extends StatelessWidget {
final int counter;
final VoidCallback incrementCounter;
const Child2({super.key, required this.counter, required this.incrementCounter});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Counter value in Child2: $counter'),
ElevatedButton(
onPressed: incrementCounter,
child: const Text('Increment Counter'),
)
],
);
}
}
- 使用状态管理库:如Provider、Bloc、MobX等。
- Provider:通过
ChangeNotifierProvider
或StreamProvider
等,在组件树中传递状态,只有依赖该状态的组件会重建。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class ProviderExample extends StatelessWidget {
const ProviderExample({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Counter(),
child: Scaffold(
appBar: AppBar(
title: const Text('Provider Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<Counter>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
),
ElevatedButton(
onPressed: () {
context.read<Counter>().increment();
},
child: const Text('Increment'),
)
],
),
),
),
);
}
}
- Bloc:通过事件驱动的方式管理状态,将业务逻辑和UI分离,减少不必要的重建。例如,在一个登录功能中,Bloc负责处理登录的业务逻辑和状态变化,UI根据Bloc的状态进行渲染。
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
// 定义事件
abstract class LoginEvent {}
class LoginButtonPressed extends LoginEvent {}
// 定义状态
abstract class LoginState {}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}
class LoginFailure extends LoginState {
final String error;
LoginFailure(this.error);
}
// Bloc类
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginInitial()) {
on<LoginButtonPressed>((event, emit) {
emit(LoginLoading());
// 模拟异步登录
Future.delayed(const Duration(seconds: 2), () {
bool success = true; // 假设登录成功
if (success) {
emit(LoginSuccess());
} else {
emit(LoginFailure('Login failed'));
}
});
});
}
}
class BlocExample extends StatelessWidget {
const BlocExample({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginBloc(),
child: Scaffold(
appBar: AppBar(
title: const Text('Bloc Example'),
),
body: Center(
child: BlocConsumer<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginFailure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error)),
);
}
},
builder: (context, state) {
if (state is LoginInitial) {
return ElevatedButton(
onPressed: () {
context.read<LoginBloc>().add(LoginButtonPressed());
},
child: const Text('Login'),
);
} else if (state is LoginLoading) {
return const CircularProgressIndicator();
} else if (state is LoginSuccess) {
return const Text('Login Success');
} else if (state is LoginFailure) {
return Text('Error: ${state.error}');
}
return Container();
},
),
),
),
);
}
}
实际项目中的应用
- 在一个电商类项目中,商品列表页面使用了状态提升。商品列表的筛选条件(如价格范围、类别等)状态被提升到父组件管理,商品列表子组件作为
StatelessWidget
,通过父组件传递筛选条件来构建列表。这样当筛选条件变化时,只有商品列表部分需要更新,而不是整个页面重建。
- 在一个社交类应用的聊天页面,使用了
AnimatedBuilder
来处理聊天消息输入框的动画。当用户点击发送按钮时,输入框会有一个缩小的动画,通过AnimatedBuilder
只更新输入框的动画部分,而不影响聊天列表等其他部分,提高了性能。
- 使用Provider进行全局状态管理,如用户登录状态。在整个应用中,多个页面依赖用户登录状态来显示不同的UI,通过
ChangeNotifierProvider
将用户登录状态提供给组件树,只有依赖该状态的组件(如显示用户信息的导航栏、登录/注销按钮等)会在登录状态变化时重建。