Weitere ähnliche Inhalte Ähnlich wie [Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023 (20) Mehr von Johnny Sung (20) [Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 20233. Full stack developer
Johnny Sung (宋岡諺)
https://fb.com/j796160836
https://blog.jks.co
ff
ee/
https://www.slideshare.net/j796160836
https://github.com/j796160836
13. class FrogColor extends InheritedWidget {
const FrogColor({
super.key,
required this.color,
required super.child,
});
final Color color;
static FrogColor? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FrogColor>();
}
static FrogColor of(BuildContext context) {
final FrogColor? result = maybeOf(context);
assert(result != null, 'No FrogColor found in context');
return result!;
}
@override
bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
}
https://api.
fl
utter.dev/
fl
utter/widgets/InheritedWidget-class.html
背後宣告
14. // continuing from previous example...
class MyPage extends StatelessWidget {
const MyPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: FrogColor(
color: Colors.green,
child: Builder(
builder: (BuildContext innerContext) {
return Text(
'Hello Frog',
style: TextStyle(color: FrogColor.of(innerContext).color),
);
},
),
),
);
}
}
https://api.
fl
utter.dev/
fl
utter/widgets/InheritedWidget-class.html
呼叫使⽤
17. class FrogColor extends InheritedWidget {
const FrogColor({
super.key,
required this.color,
required super.child,
});
final Color color;
static FrogColor? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FrogColor>();
}
static FrogColor of(BuildContext context) {
final FrogColor? result = maybeOf(context);
assert(result != null, 'No FrogColor found in context');
return result!;
}
@override
bool updateShouldNotify(FrogColor oldWidget) => color != oldWidget.color;
}
https://api.
fl
utter.dev/
fl
utter/widgets/InheritedWidget-class.html
(剛剛看到的段落)
18. class Provider<T> extends InheritedProvider<T> {
Provider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
lazy: lazy,
builder: builder,
create: create,
dispose: dispose,
debugCheckInvalidValueType: kReleaseMode
? null
: (T value) =>
Provider.debugCheckInvalidValueType?.call<T>(value),
child: child,
);
static T of<T>(BuildContext context, {bool listen = true}) {
assert(
context.owner!.debugBuilding ||
listen == false ||
debugIsInInheritedProviderUpdate,
'''
Tried to listen to a value exposed with provider, from outside of the widget tree. … 略
''',
);
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {
context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
}
final value = inheritedElement?.value;
if (_isSoundMode) {
if (value is! T) {
throw ProviderNullException(T, context.widget.runtimeType);
}
return value;
}
return value as T;
}
}
Provider 的原始碼
20. Provider 家族
• Provider:提供遠端存取的服務
• ChangeNoti
fi
erProvider:它會監聽 model 的變化。當資料發⽣變更,它會重繪
Consumer 的⼦ widget。
• FutureProvider:是 FutureBuilder 的⼀個封裝,提供⼀個 Future,FutureProvider 會在 Future 完成的時候
通知 Consumer 重繪它的 Widget ⼦樹。
• StreamProvider:是 StreamBuilder 的⼀個封裝,提供⼀個 Stream,然後 Consumer 會在 Stream 收到事
件時更新它的 Widget⼦樹。
22. import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider<Counter>(
create: (context) => Counter(),
child: Builder(builder: (context) {
final counter = context.watch<Counter>().count;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('$counter'),
ElevatedButton(
onPressed: () {
final counter = context.read<Counter?>();
counter?.increment();
print('count=${counter?.count ?? 0}');
},
child: const Text('Push me')),
],
),
);
}),
),
);
}
}
24. /// Exposes the [read] method.
extension ReadContext on BuildContext {
T read<T>() {
return Provider.of<T>(this, listen: false);
}
}
Provider 原始碼中
context.read() 對應到 Provider.of()
27. mixin & with
Mixins are a way of reusing a class’s code in multiple class hierarchies.
簡單來說就是:
我提供⼀些⽅法給你使⽤,但我不⽤成為你的⽗類別。
mixin ShowName {
void showName() {
print('mixin ShowName');
}
}
class Quadrilateral with ShowName {
void showAllSideLength() {
showName();
}
}
https://ithelp.ithome.com.tw/articles/10268368?sc=iThelpR
28. import 'package:flutter/material.dart';
import 'package:flutter_provider_tab_demo/tab_provider.dart';
import 'package:flutter_provider_tab_demo/tab_screen.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => TabProvider()),
],
child: TabScreen(),
),
);
}
}
29. import 'package:flutter/material.dart';
import 'package:flutter_provider_tab_demo/tab_provider.dart';
import 'package:provider/provider.dart';
class TabItem {
final String name;
final int index;
TabItem(this.name, this.index);
}
class TabScreen extends StatelessWidget {
final List<TabItem> tabItems = [
TabItem('Tab 1', 0),
TabItem('Tab 2', 1),
TabItem('Tab 3', 2),
];
TabScreen({super.key});
@override
Widget build(BuildContext context) {
// build
}
}
return Scaffold(
appBar: AppBar(
title: const Text('Tab Example'),
),
body: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: tabItems.length,
itemBuilder: (context, index) {
TabItem tabItem = tabItems[index];
return ListTile(
title: Text(tabItem.name),
onTap: () {
TabProvider p = context.read<TabProvider>();
p.currentIndex = tabItem.index;
},
);
},
),
Expanded(
child: IndexedStack(
index: context.watch<TabProvider>().currentIndex,
children: const [
Center(child: Text('Tab 1')),
Center(child: Text('Tab 2')),
Center(child: Text('Tab 3')),
],
),
),
],
),
);
選單按鈕
分⾴內⾴
35. MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider<SigninCubit>(
create: (context) => SigninCubit(),
child: BlocProvider<UpdateCubit>(
create: (context) => UpdateCubit(),
child: BlocProvider<ProfileBloc>(
create: (context) => ProfileBloc(),
child: BlocProvider<VoteBloc>(
create: (context) => VoteBloc(),
child: BlocProvider<MessageBloc>(
create: (context) => MessageBloc(),
child: BlocProvider<LeaderboardBloc>(
create: (context) => LeaderboardBloc(),
child: BlocProvider<CommentBloc>(
create: (context) => CommentBloc(),
child: BlocProvider<EditbioCubit>(
create: (context) => EditbioCubit(),
child: BlocProvider<CommentCubit>(
create: (context) => CommentCubit(),
child: BlocProvider<SearchBloc>(
create: (context) => SearchBloc(),
child: BlocProvider<SwipeBloc>(
create: (context) => SwipeBloc(),
child: Scaffold(
body: Builder(builder: (context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Sample'),
],
),
);
}),
),
),
),
),
),
),
),
),
),
),
),
),
);
不免俗的,Flutter 沒寫好的話,很容易寫出⼀堆波動拳
https://captown.capcom.com/zh-TW/museums/street
fi
ghter/5/images/125#group=0&photo=0
36. 部分的波動拳可以⽤ MultiXXXProvider 解決部分的問題
但這範例還有權責不明確問題
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiBlocProvider(
providers: [
BlocProvider<SigninCubit>(create: (context) => SigninCubit()),
BlocProvider<UpdateCubit>(create: (context) => UpdateCubit()),
BlocProvider<ProfileBloc>(create: (context) => ProfileBloc()),
BlocProvider<VoteBloc>(create: (context) => VoteBloc()),
BlocProvider<MessageBloc>(create: (context) => MessageBloc()),
BlocProvider<LeaderboardBloc>(create: (context) => LeaderboardBloc()),
BlocProvider<CommentBloc>(create: (context) => CommentBloc()),
BlocProvider<EditbioCubit>(create: (context) => EditbioCubit()),
BlocProvider<CommentCubit>(create: (context) => CommentCubit()),
BlocProvider<SearchBloc>(create: (context) => SearchBloc()),
BlocProvider<SwipeBloc>(create: (context) => SwipeBloc()),
],
child: Scaffold(
body: Builder(builder: (context) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Sample'),
],
),
);
}),
),
),
);
40. 當 Bloc 遇上 provider ?
http://teddy-chen-tw.blogspot.com/2012/03/blog-post_20.html
爭什麼,摻在⼀起做成撒尿⽜丸啊
46. BlocBuilder<LoginBloc, LoginState>(builder: (context, state) {
if (state is LoginStateLoggedIn) {
return …;
}
if (state is LoginStateInitial) {
return …;
}
return Container();
}
在所需 Widget 的 build() 處使⽤ BlocBuilder
(以下是⼀些 pseudo code)
BlocProvider 使⽤⽅式 (2/4)
這裡沒指定 bloc 參數時,預設會 Provider 往上找
47. 在需要聆聽事件的地⽅使⽤ BlocListener
(以下是⼀些 pseudo code)
BlocProvider 使⽤⽅式 (3/4)
這裡沒指定 bloc 參數時,預設會 Provider 往上找
BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginStateError) {
// Do Something
}
if (state is LoginStateLoggedIn) {
// Do Something
}
},
child: … );
48. 在需要變更狀態的地⽅使⽤ context.read() 並操作 Bloc
(以下是⼀些 pseudo code)
BlocProvider 使⽤⽅式 (4/4)
ElevatedButton(
child: const Text('Login'),
onPressed: () {
context.read<LoginBloc>().add(LoginEventLogin());
},
)