SlideShare a Scribd company logo
Flutter Provider
看似簡單卻⼜不簡單的
狀態管理⼯具
Kaohsiung
Johnny Sung
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
Full stack developer
Johnny Sung (宋岡諺)
https://fb.com/j796160836
https://blog.jks.co
ff
ee/
https://www.slideshare.net/j796160836
https://github.com/j796160836
https://pub.dev/packages/provider
講在 Provider 之前
— InheritedWidget
Kaohsiung
https://www.youtube.com/watch?v=Zbm3hjPjQMk
在 Flutter 的世界裡...
萬物皆 Widget
在對應的狀態產⽣對應的畫⾯
https://abhishekdoshi26.medium.com/deep-dive-into-
fl
utter-trees-542f7395df5c
Widget 兄弟其實還少⼀個
• StatefulWidget
• StatelessWidget
• InheritedWidget 就是我啦!
InheritedWidget
想解決
Widget tree 跨層傳遞資料 不便的問題
https://docs.
fl
utter.dev/data-and-backend/state-mgmt/simple
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
背後宣告
// 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
呼叫使⽤
Provider
就是 InheritedWidget 的延伸(與包裝)
其實
不信?讓我們看看⼀⼩段 Provider 的原始碼
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
(剛剛看到的段落)
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 的原始碼
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
Provider 家族
• Provider:提供遠端存取的服務
• ChangeNoti
fi
erProvider:它會監聽 model 的變化。當資料發⽣變更,它會重繪
Consumer 的⼦ widget。
• FutureProvider:是 FutureBuilder 的⼀個封裝,提供⼀個 Future,FutureProvider 會在 Future 完成的時候
通知 Consumer 重繪它的 Widget ⼦樹。
• StreamProvider:是 StreamBuilder 的⼀個封裝,提供⼀個 Stream,然後 Consumer 會在 Stream 收到事
件時更新它的 Widget⼦樹。
import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
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')),
],
),
);
}),
),
);
}
}
Consumer<Counter>(builder: (context, myModel, child) {
return Text('${myModel.count}');
}),
或
三種寫法都可以
註:Consumer 的部份
Text('${context.watch<Counter>().count}')
Text('${Provider.of<Counter>(context).count}')
或
/// Exposes the [read] method.
extension ReadContext on BuildContext {
T read<T>() {
return Provider.of<T>(this, listen: false);
}
}
Provider 原始碼中
context.read() 對應到 Provider.of()
選單按鈕
分⾴內⾴
import 'package:flutter/foundation.dart';
class TabProvider with ChangeNotifier {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
set currentIndex(int index) {
_currentIndex = index;
notifyListeners();
}
}
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
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(),
),
);
}
}
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')),
],
),
),
],
),
);
選單按鈕
分⾴內⾴
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
Provider 使⽤情境
• 會員帳號權限管理
• 帳號登入
• Token 過期、被強迫登出
• Widget tree 平⾏傳遞資料 (例如:分⾴切換)
全域的狀態機變數
Provider 我的理解:
全域變數
雖可恥但有⽤
https://stackover
fl
ow.com/questions/73226745/
fl
utter-many-bloc-providers-cluttering-widget-tree
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
部分的波動拳可以⽤ 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'),
],
),
);
}),
),
),
);
要慎⽤呀!
https://pic1.zhimg.com/80/v2-5af8d68f267c1ed3ece59d5bd919a870_1440w.webp
解決波動拳⼩建議
• View 分類好封裝進 StatelessWidget
• 邏輯與 View 分類分開存放
• ⽤ MultiProvider 或 MultiBlocProvider 等類似套件來拉平
Q: Provider 與 Bloc 有什麼差異?
我的回答:要解決的問題不同,使⽤不同的解決⽅式
當 Bloc 遇上 provider ?
http://teddy-chen-tw.blogspot.com/2012/03/blog-post_20.html
爭什麼,摻在⼀起做成撒尿⽜丸啊
https://pub.dev/packages/
fl
utter_bloc
https://pub.dev/packages/
fl
utter_bloc
😜
BlocProvider = Bloc + Provider
BlocProvider 家族
• BlocProvider
• BlocBuilder
• BlocListener
• BlocConsumer = BlocBuilder +
BlocListener
BlocProvider<LoginBloc>(
create: (BuildContext context) => LoginBloc()),
child: …,
),
在 Widget Tree 最頂層處使⽤ BlocProvider 並宣告⾃訂的 Bloc
(以下是⼀些 pseudo code)
BlocProvider 使⽤⽅式 (1/4)
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 往上找
在需要聆聽事件的地⽅使⽤ 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: … );
在需要變更狀態的地⽅使⽤ context.read() 並操作 Bloc
(以下是⼀些 pseudo code)
BlocProvider 使⽤⽅式 (4/4)
ElevatedButton(
child: const Text('Login'),
onPressed: () {
context.read<LoginBloc>().add(LoginEventLogin());
},
)
https://docs.
fl
utter.dev/data-and-backend/state-mgmt/simple
Q&A
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
其他狀態管理比較
Kaohsiung
https://k.sina.cn/article_7407912364_1b98bc5ac00101jqaw.html?from=cul
https://pub.dev/packages/mobx
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
mobx_codegen
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
https://pub.dev/packages/scoped_model
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
https://pub.dev/packages/get
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
https://pub.dev/packages/riverpod
[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023
感謝聆聽

More Related Content

PDF
Jwt Security
PDF
OpenID Connect入門
PPTX
Building Secure User Interfaces With JWTs
PDF
[OPD 2019] Attacking JWT tokens
PPTX
Integration testing for microservices with Spring Boot
PDF
Json web token
PDF
Introduction to JWT and How to integrate with Spring Security
PPTX
Super keyword in java
Jwt Security
OpenID Connect入門
Building Secure User Interfaces With JWTs
[OPD 2019] Attacking JWT tokens
Integration testing for microservices with Spring Boot
Json web token
Introduction to JWT and How to integrate with Spring Security
Super keyword in java

What's hot (20)

PPTX
Json Web Token - JWT
PPTX
Pentesting jwt
PDF
Measurement of Maximum new NAT-sessions per second / How to send packets
PDF
JSON Web Token
PDF
ゼロから学ぶIoTハンズオン資料
PDF
RESTfulとは
PDF
Making Nested Columns as First Citizen in Apache Spark SQL
PPTX
リアルタイムサーバー 〜Erlang/OTPで作るPubSubサーバー〜
PDF
MySQLとPostgreSQLと日本語全文検索 - Azure DatabaseでMroonga・PGroongaを使いたいですよね!?
PDF
AWS Redshift Analyzeの必要性とvacuumの落とし穴
PDF
Azure Network 概要
PPTX
SQLチューニング入門 入門編
PPTX
Php cookies
DOCX
系統程式 -- 第 8 章 編譯器
PDF
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
PPT
7.data types in c#
PPTX
Dynamic Routing Protocol OSPF
PDF
Hazelcast
PPT
JavaScript Variables
PPTX
Web scraping with BeautifulSoup, LXML, RegEx and Scrapy
Json Web Token - JWT
Pentesting jwt
Measurement of Maximum new NAT-sessions per second / How to send packets
JSON Web Token
ゼロから学ぶIoTハンズオン資料
RESTfulとは
Making Nested Columns as First Citizen in Apache Spark SQL
リアルタイムサーバー 〜Erlang/OTPで作るPubSubサーバー〜
MySQLとPostgreSQLと日本語全文検索 - Azure DatabaseでMroonga・PGroongaを使いたいですよね!?
AWS Redshift Analyzeの必要性とvacuumの落とし穴
Azure Network 概要
SQLチューニング入門 入門編
Php cookies
系統程式 -- 第 8 章 編譯器
Python Flask Tutorial For Beginners | Flask Web Development Tutorial | Python...
7.data types in c#
Dynamic Routing Protocol OSPF
Hazelcast
JavaScript Variables
Web scraping with BeautifulSoup, LXML, RegEx and Scrapy
Ad

Similar to [Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023 (20)

PDF
Architecting your GWT applications with GWT-Platform - Lesson 02
PPTX
Mobile Application Development class 006
PPTX
Force Flutter to Rebuild or Redraw All Widgets.pptx
PDF
Developer Student Clubs NUK - Flutter for Beginners
PPT
Google Web Toolkits
PDF
Flutter State Management Using GetX.pdf
PDF
Qt & Webkit
PDF
Building a js widget
PDF
React & The Art of Managing Complexity
PDF
Android Best Practices
PDF
Overview of The Scala Based Lift Web Framework
PDF
Scala based Lift Framework
PDF
Overview Of Lift Framework
PDF
Binding business data to vaadin components
PPT
GWT MVP Case Study
PDF
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
PDF
Overview of Android Infrastructure
PDF
Overview of Android Infrastructure
PPTX
SoftShake 2013 - Vaadin componentization
PDF
Decoupling with Design Patterns and Symfony2 DIC
Architecting your GWT applications with GWT-Platform - Lesson 02
Mobile Application Development class 006
Force Flutter to Rebuild or Redraw All Widgets.pptx
Developer Student Clubs NUK - Flutter for Beginners
Google Web Toolkits
Flutter State Management Using GetX.pdf
Qt & Webkit
Building a js widget
React & The Art of Managing Complexity
Android Best Practices
Overview of The Scala Based Lift Web Framework
Scala based Lift Framework
Overview Of Lift Framework
Binding business data to vaadin components
GWT MVP Case Study
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
Overview of Android Infrastructure
Overview of Android Infrastructure
SoftShake 2013 - Vaadin componentization
Decoupling with Design Patterns and Symfony2 DIC
Ad

More from Johnny Sung (20)

PDF
用 Gemma 3, Gemma 3n 開放模型來解決企業難解的問題 @ COSCUP 2025
PDF
[GDG Build with AI] 善用現代 AI 科技:打造專屬行銷工具箱 @ GDG Changhua 彰化
PDF
地端自建 Kubernetes (K8s) 小宇宙 (On-premises Kubernetes) @ CNTUG 2024/11 Meetup #63
PDF
[AI LLM] Gemma 初體驗 @ GDG Cloud Taipei Meetup #70
PDF
Kubernetes 地端自建 v.s. GKE,哪個更適合你? @Devfest Taipei 2024
PDF
ArgoCD 的雷 碰過的人就知道 @TSMC IT Community Meetup #4
PDF
使用 Kong 與 GitOps 來管理您企業的 API 呼叫 @ 2024 台灣雲端大會
PDF
[AI / ML] 用 LLM (Large language model) 來整理您的知識庫 @Devfest Taipei 2023
PDF
[Golang] 以 Mobile App 工程師視角,帶你進入 Golang 的世界 (Introduction of GoLang)
PDF
[Flutter] 來體驗 bloc 小方塊的神奇魔法 @Devfest 2022
PDF
與 Sign in with Apple 的愛恨情仇 @ iPlayground2020
PDF
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
PDF
談談 Android constraint layout
PDF
炎炎夏日學 Android 課程 - Part3: Android app 實作
PDF
炎炎夏日學 Android 課程 - Part1: Kotlin 語法介紹
PDF
炎炎夏日學 Android 課程 - Part2: Android 元件介紹
PDF
炎炎夏日學 Android 課程 - Part 0: 環境搭建
PPTX
About Mobile Accessibility
PDF
Introductions of Messaging bot 做聊天機器人
PDF
First meet with Android Auto
用 Gemma 3, Gemma 3n 開放模型來解決企業難解的問題 @ COSCUP 2025
[GDG Build with AI] 善用現代 AI 科技:打造專屬行銷工具箱 @ GDG Changhua 彰化
地端自建 Kubernetes (K8s) 小宇宙 (On-premises Kubernetes) @ CNTUG 2024/11 Meetup #63
[AI LLM] Gemma 初體驗 @ GDG Cloud Taipei Meetup #70
Kubernetes 地端自建 v.s. GKE,哪個更適合你? @Devfest Taipei 2024
ArgoCD 的雷 碰過的人就知道 @TSMC IT Community Meetup #4
使用 Kong 與 GitOps 來管理您企業的 API 呼叫 @ 2024 台灣雲端大會
[AI / ML] 用 LLM (Large language model) 來整理您的知識庫 @Devfest Taipei 2023
[Golang] 以 Mobile App 工程師視角,帶你進入 Golang 的世界 (Introduction of GoLang)
[Flutter] 來體驗 bloc 小方塊的神奇魔法 @Devfest 2022
與 Sign in with Apple 的愛恨情仇 @ iPlayground2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
談談 Android constraint layout
炎炎夏日學 Android 課程 - Part3: Android app 實作
炎炎夏日學 Android 課程 - Part1: Kotlin 語法介紹
炎炎夏日學 Android 課程 - Part2: Android 元件介紹
炎炎夏日學 Android 課程 - Part 0: 環境搭建
About Mobile Accessibility
Introductions of Messaging bot 做聊天機器人
First meet with Android Auto

[Flutter] Flutter Provider 看似簡單卻又不簡單的狀態管理工具 @ Devfest Kaohsiung 2023