InheritedWidget
是一個功能型組件,並且提供了一種在widget tree中從上到下共享數據的方法。[Intro]
當我們在一個widget中使用InheritedWidget共享了一個數據,那麼我們便可以在該widget下的任意子widget中來獲取該共享的數據。(與react中的context功能類似,通過數據從上而下來進行傳遞數據)
運作原理:
我們打開flutter\packages\flutter\lib\src\widgets\framework.dart
我們能看到InheritedWidget透過updateShouldNotify
來決定是否應通知從該widget繼承的小widget要重新build。
測試
我們回到我們的flutter poject
首先,我們創建一個InheritedWidget,並命名為user_header_component.dartuser_header_component.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
class UserHeader extends InheritedWidget {
final bool isLogin;
const UserHeader({
Key? key,
required this.isLogin,
required Widget child,
}) : super(key: key, child: child);
// 定義一個方便子樹中的widget獲取共享數據的方法
static UserHeader? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserHeader>();
}
@override
bool updateShouldNotify(UserHeader oldWidget) {
return oldWidget.isLogin != isLogin;
}
}
我們打開我們的todo\lib\ui\screens\user_screen.dart
並添加在State
class _UserScreenState extends State<UserScreen> {
...
bool _isLogin = false;
@override
Widget build(BuildContext context) {
...
child:Column(
children: [
UserHeader(
isLogin: _isLogin,
child: Container(
child: _TestWidget(),
),
),
TextButton(
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
child: Text('set login'))
],
),
...
}
class _TestWidget extends StatefulWidget {
const _TestWidget({Key? key}) : super(key: key);
@override
State<_TestWidget> createState() => __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
final Color _color = Color(Random().nextInt(0xffffffff));
@override
Widget build(BuildContext context) {
return Container(
color: _color,
child: Text(
'Login: ' + UserHeader.of(context)!.isLogin.toString(),
),
);
}
}
這裡,我們能看到他會一直呼叫print,並且只對有使用該數據的Widget更新,所以__TestWidgetState裡的Container沒有被rebuild(如果有的話顏色會變)
接著,我們小小修改一下_TestWidget
class __TestWidgetState extends State<_TestWidget> {
final Color _color = Color(Random().nextInt(0xffffffff));
@override
Widget build(BuildContext context) {
return Container(
color: _color,
// child: Text(
// 'Login: ' + UserHeader.of(context)!.isLogin.toString(),
// ),
child: Text('test'),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 當build有使用到InheritedWidget,就會呼叫
print("Dependencies change");
}
}
我們能看到當點擊按鈕後,雖然我們的UserHeadert
的data雖然發生變化。
但是現在我們的_TestWidgetState並未依賴UserHeadert,所以_TestWidgetState不會呼叫didChangeDependencies方法。此機制是在數據發生變化時只對使用該數據的Widget更新
[上述例子參考《Flutter實戰·第二版》]
今天簡單的介紹了InheritedWidget,明天會開始介紹Provider
// user_screen.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:todo/ui/components/user_header_component.dart';
class UserScreen extends StatefulWidget {
const UserScreen({Key? key}) : super(key: key);
@override
State<UserScreen> createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen> {
final TrackingScrollController _trackingScrollController =
TrackingScrollController();
bool _isLogin = false;
@override
Widget build(BuildContext context) {
return CustomScrollView(
controller: _trackingScrollController,
slivers: [
SliverAppBar(
title: Text('User Screen'),
),
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.all(8.0),
child: Row(children: [
Placeholder(
fallbackHeight: 50,
fallbackWidth: 50,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
UserHeader(
isLogin: _isLogin,
child: Container(
child: _TestWidget(),
),
),
TextButton(
onPressed: () {
setState(() {
_isLogin = !_isLogin;
});
},
child: Text('set login'))
],
),
),
]),
),
),
],
);
}
}
class _TestWidget extends StatefulWidget {
const _TestWidget({Key? key}) : super(key: key);
@override
State<_TestWidget> createState() => __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
final Color _color = Color(Random().nextInt(0xffffffff));
@override
Widget build(BuildContext context) {
return Container(
color: _color,
child: Text(
'Login: ' + UserHeader.of(context)!.isLogin.toString(),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 當build有使用到InheritedWidget,就會呼叫
print("Dependencies change");
}
}
// user_header_component.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
class UserHeader extends InheritedWidget {
final bool isLogin;
const UserHeader({
Key? key,
required this.isLogin,
required Widget child,
}) : super(key: key, child: child);
// 定義一個方便子樹中的widget獲取共享數據的方法
static UserHeader? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<UserHeader>();
}
@override
bool updateShouldNotify(UserHeader oldWidget) {
return oldWidget.isLogin != isLogin;
}
}