在flutter中濫用statefulWidget將導致不可知的一些問題,其中一個問題就是會導致不停的rebuild整個頁面,設想一下,你要是隻需要點選一個按鈕,更新一個按鈕,卻在點選這個按鈕的時候,rebuild了整個頁面,豈不是對效能的過大消耗? 為了解決這個問題,和直觀的感覺這個問題,我引入了provider這個外掛來作說明。 先上效果:
按第一個區域的按鈕:
按下第二個區域後再按下第三個區域:
這裡我用了三塊區域來作說明:這三塊區域都是使用的隨機顏色,每次rebuild都會改變這些顏色。 首先,第一個區域是使用provider外掛的,點選了按鈕,counter會加1,但背景色沒有做改變。 第二個區域使用的資料來自於最外面的statefulWidget,點選了按鈕之後整個頁面將會rebuild。 第三個區域是相當於一個葉子節點,它的counter資料來自於自己,點選按鈕之後,這個區域會發生rebuild即只更新這個區域。 程式碼如下:
import 'dart:math';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'models/counter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CounterModel(0)),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Test()
)
);
}
}
class Test extends StatefulWidget {
@override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
// 這個counter將在第二區域中使用
int counter = 0;
_incrementCounter(){
setState(() {
++counter;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
constraints: BoxConstraints.expand(),
child: Column(
children: <Widget>[
Counter1(
title: '使用provider不會觸發rebuild'
),
Counter2(
counter: counter,
onClick: _incrementCounter,
title: '使用的是最外層的資料,改變會觸發整個頁面的rebuild',
),
Counter3(
title: '葉子節點,使用的自己內部的資料,自身rebuild不會影響外面',
)
],
),
),
),
);
}
}
// 首先,第一個區域是使用provider外掛的,點選了按鈕,counter會加1,但背景色沒有做改變
class Counter1 extends StatelessWidget {
final counter;
final title;
Counter1({this.counter,this.title});
BuildContext ctx;
@override
Widget build(BuildContext context) {
ctx = context;
Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
return Container(
child: Consumer(builder: (BuildContext context, CounterModel counterModel, Widget child){
return AnimatedContainer(
duration: Duration(seconds: 1),
color: color,
height: 150,
child: Column(
children: <Widget>[
Text(title,style: TextStyle(fontSize: 30)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('counter= ${counterModel.count}')
],
),
RaisedButton(
color: color,
textColor: Colors.white,
elevation: 20,
child: Text('點我增加'),
onPressed: _increment
)
],
),
);
})
);
}
void _increment() {
Provider.of<CounterModel>(ctx,listen: false).increment();
print(Provider.of<CounterModel>(ctx,listen: false).count);
}
}
// 第二個區域使用的資料來自於最外面的statefulWidget,點選了按鈕之後整個頁面將會rebuild。
class Counter2 extends StatelessWidget {
final VoidCallback onClick;
final int counter;
final String title;
Counter2({
Key key,
this.counter,
this.onClick,
this.title
});
@override
Widget build(BuildContext context) {
Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
return AnimatedContainer(
duration: Duration(milliseconds: 500),
color:color,
height: 250,
child:Column(
children: <Widget>[
Text(title,style: TextStyle(fontSize: 30),),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('counter = ${this.counter}',style: TextStyle(fontSize: 43,color: Colors.white),),
],
),
RaisedButton(
color: color,
textColor: Colors.white,
elevation: 20,
onPressed: onClick,
child: Text('increment Counter'),
),
],
),
);
}
}
// 第三個區域是相當於一個葉子節點,它的counter資料來自於自己,點選按鈕之後,這個區域會發生rebuild即只更新這個區域。
class Counter3 extends StatefulWidget {
final title;
Counter3({
Key key,
this.title
}): super(key: key);
@override
_Counter3State createState() => _Counter3State();
}
class _Counter3State extends State<Counter3> {
int counter = 0;
@override
Widget build(BuildContext context) {
Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
return AnimatedContainer(
duration: Duration(milliseconds: 500),
color:color,
height: 250,
child:Column(
children: <Widget>[
Text(widget.title,style: TextStyle(fontSize: 30)),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('counter = $counter',style: TextStyle(fontSize: 43,color: Colors.white),),
],
),
RaisedButton(
color: color,
textColor: Colors.white,
elevation: 20,
onPressed: _click,
child: Text('increment Counter'),
),
],
),
);
}
_click() {
setState(() {
++counter;
});
}
}
複製程式碼
第一個區域中的model檔案:
import 'package:flutter/material.dart';
class CounterModel with ChangeNotifier {
int _count;
CounterModel(this._count);
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void setValue(int val) {
_count = val;
notifyListeners();
}
}
複製程式碼