Slide 1

Slide 1 text

riverpodを理解したい pixiv Inc. tatsubee 2023.10.26

Slide 2

Slide 2 text

2 自己紹介 ● 23新卒 ● 福岡生まれ 福岡育ち 東京在住 ● 最近やっていること ○ お絵描き ○ テニス ○ iOS tatsubee iOSエンジニア

Slide 3

Slide 3 text

3 初めにちょっとだけ宣伝

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

5 福岡のFlutterコミュニティ 様々なイベントを開催して活発に活動中! ● 月一でのLTイベント開催 ● 先日の東京Flutterハッカソンへの参加 ● その他内輪の勉強会 などなど

Slide 6

Slide 6 text

6 福岡のFlutterコミュニティ 様々なイベントを開催して活発に活動中!!! ● 月一でのLTイベント開催 ● 東京Flutterハッカソンへの参加 ● その他内輪の勉強会 などなど

Slide 7

Slide 7 text

7 本題

Slide 8

Slide 8 text

8 riverpod勉強会 Fukuoka Flutter Fanclubで毎週やっている勉強会 目標: riverpodを「作れる」レベルのコード理解 ↓を順に、1時間でできる範囲でドキュメントやコードを読み進めていく ● riverpodでできることの把握 ● InheritedWidgetのコード理解 ● providerのコード理解 ● riverpodのコード理解

Slide 9

Slide 9 text

9 riverpod勉強会 Fukuoka Flutter Fanclubで毎週やっている勉強会 目標: riverpodを「作れる」レベルのコード理解 ↓を順に、1時間でできる範囲でドキュメントやコードを読み進めていく ● riverpodでできることの把握 ● InheritedWidgetのコード理解 ←ここまで進んだ ● providerのコード理解 ● riverpodのコード理解

Slide 10

Slide 10 text

10 このLTの内容 riverpod

Slide 11

Slide 11 text

11 このLTの内容 riverpod InheritedWidget

Slide 12

Slide 12 text

12 InheritedWidget 特徴 ● 祖先のWidgetが管理する値にO(1)でアクセスできる ● 値を購読する子孫のWidgetに、その値の変更を通知することができる (riverpodやproviderがあるので直接扱う必要性はない)

Slide 13

Slide 13 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 13 InheritedWidget

Slide 14

Slide 14 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 14 InheritedWidget class Child extends StatelessWidget { const Child({super.key}); @override Widget build(BuildContext context) => Text('${CountInherited.of(context).count}'); }

Slide 15

Slide 15 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 15 InheritedWidget class CountInherited extends InheritedWidget { const CountInherited({ super.key, required this.count, required super.child, }); final int count; static CountInherited of(BuildContext context) => context.dependOnInheritedWidgetOfExactType()!; @override bool updateShouldNotify(CountInherited oldWidget) => oldWidget.count != count; }

Slide 16

Slide 16 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 16 InheritedWidget

Slide 17

Slide 17 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 17 InheritedWidget

Slide 18

Slide 18 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 18 InheritedWidget

Slide 19

Slide 19 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 19 InheritedWidget

Slide 20

Slide 20 text

20 InheritedWidgetの 中身を覗いてみる

Slide 21

Slide 21 text

abstract class InheritedWidget extends ProxyWidget { const InheritedWidget({ super.key, required super.child }); @override InheritedElement createElement() => InheritedElement(this); @protected bool updateShouldNotify(covariant InheritedWidget oldWidget); } 21 InheritedElementの作成と 変更の通知の判定のみ!

Slide 22

Slide 22 text

class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget super.widget); final Map _dependents = HashMap(); @override void _updateInheritance() {...} @override void debugDeactivated() {...} @protected Object? getDependencies(Element dependent) {...} @protected void updateDependencies(Element dependent, Object? aspect) {...} @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {...} @override void updated(InheritedWidget oldWidget) {...} @override void notifyClients(InheritedWidget oldWidget) {...} } 22

Slide 23

Slide 23 text

23 O(1)でアクセスする仕組み

Slide 24

Slide 24 text

24 アクセス方法

Slide 25

Slide 25 text

abstract class Element extends DiagnosticableTree implements BuildContext { PersistentHashMap? _inheritedElements; @override T? dependOnInheritedWidgetOfExactType({ Object? aspect }) { final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T]; if (ancestor != null) { return dependOnInheritedElement(ancestor, aspect: aspect) as T; } _hadUnsatisfiedDependencies = true; return null; } } 25 = watch

Slide 26

Slide 26 text

abstract class Element extends DiagnosticableTree implements BuildContext { PersistentHashMap? _inheritedElements; @override T? dependOnInheritedWidgetOfExactType({ Object? aspect }) { final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T]; if (ancestor != null) { return dependOnInheritedElement(ancestor, aspect: aspect) as T; } _hadUnsatisfiedDependencies = true; return null; } } 26 自身が持っている_inheritedElements の中から 型が一致するInheritedElementを返す = watch

Slide 27

Slide 27 text

27 _inheritedElementsへの格納

Slide 28

Slide 28 text

abstract class Element extends DiagnosticableTree implements BuildContext { @mustCallSuper void mount(Element? parent, Object? newSlot) { _updateInheritance(); } } 28

Slide 29

Slide 29 text

abstract class Element extends DiagnosticableTree implements BuildContext { void _updateInheritance() { _inheritedElements = _parent?._inheritedElements; } } class InheritedElement extends ProxyElement { @override void _updateInheritance() { final PersistentHashMap incomingWidgets = _parent?._inheritedElements ?? const PersistentHashMap.empty(); _inheritedElements = incomingWidgets.put(widget.runtimeType, this); } } 29

Slide 30

Slide 30 text

abstract class Element extends DiagnosticableTree implements BuildContext { void _updateInheritance() { _inheritedElements = _parent?._inheritedElements; } } class InheritedElement extends ProxyElement { @override void _updateInheritance() { final PersistentHashMap incomingWidgets = _parent?._inheritedElements ?? const PersistentHashMap.empty(); _inheritedElements = incomingWidgets.put(widget.runtimeType, this); } } 30 親の_inheritedElenentsを そのまま引き継ぐ

Slide 31

Slide 31 text

abstract class Element extends DiagnosticableTree implements BuildContext { void _updateInheritance() { _inheritedElements = _parent?._inheritedElements; } } class InheritedElement extends ProxyElement { @override void _updateInheritance() { final PersistentHashMap incomingWidgets = _parent?._inheritedElements ?? const PersistentHashMap.empty(); _inheritedElements = incomingWidgets.put(widget.runtimeType, this); } } 31 親から引き継いだ _inheritedElenentsに 自身を挿入する

Slide 32

Slide 32 text

32 視覚的に見てみると...

Slide 33

Slide 33 text

33 Parent CounterInherited Child Widgetツリー { } { CounterInherited: InheritedElement } _inheritedElements { CounterInherited: InheritedElement }

Slide 34

Slide 34 text

34 Parent CounterInherited Child Widgetツリー { } { CounterInherited: InheritedElement } _inheritedElements { CounterInherited: InheritedElement } アクセス

Slide 35

Slide 35 text

35 変更を通知する仕組み

Slide 36

Slide 36 text

36 countの値を変更する時

Slide 37

Slide 37 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 37

Slide 38

Slide 38 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 38 リビルド範囲

Slide 39

Slide 39 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 39 リビルド範囲

Slide 40

Slide 40 text

class Parent extends StatefulWidget { … } class _ParentState extends State { int count = 0; @override Widget build(BuildContext context) { return CountInherited( count: count, child: Scaffold( body: const Center( child: Child(), ), floatingActionButton: FloatingActionButton( onPressed: () => setState(() { count++; }), ), ), ); } } 40 リビルド範囲 リビルドされない constによってリビルドが抑 制される

Slide 41

Slide 41 text

class Child extends StatelessWidget { const Child({super.key}); @override Widget build(BuildContext context) => Text('${CountInherited.of(context).count}'); } 41

Slide 42

Slide 42 text

class Child extends StatelessWidget { const Child({super.key}); @override Widget build(BuildContext context) => Text('${CountInherited.of(context).count}'); } 42 StatefulWidgetの方が わかりやすいので変換

Slide 43

Slide 43 text

class Child extends StatefulWidget { … } class _ChildState extends State { @override void didChangeDependencies() { … } @override Widget build(BuildContext context) { final countInherited = context.dependOnInheritedWidgetOfExactType()! as CountInherited; return Text('${countInherited.count}); } } 43 StatefulWidgetの方が わかりやすいので変換

Slide 44

Slide 44 text

class Child extends StatefulWidget { … } class _ChildState extends State { @override void didChangeDependencies() { … } @override Widget build(BuildContext context) { final countInherited = context.dependOnInheritedWidgetOfExactType()! as CountInherited; return Text('${countInherited.count}); } } 44 setStateされると didChangeDependencies が発火!

Slide 45

Slide 45 text

class Child extends StatefulWidget { … } class _ChildState extends State { @override void didChangeDependencies() { … } @override Widget build(BuildContext context) { final countInherited = context.dependOnInheritedWidgetOfExactType()! as CountInherited; return Text('${countInherited.count}); } } 45 ”Dependencies”とは、 InheritedWidgetのこと!

Slide 46

Slide 46 text

46 時間がなさそうなのでここまで! 気になる方は懇親会で!

Slide 47

Slide 47 text

class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget super.widget); final Map _dependents = HashMap(); @override void _updateInheritance() {...} @override void debugDeactivated() {...} @protected Object? getDependencies(Element dependent) {...} @protected void updateDependencies(Element dependent, Object? aspect) {...} @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {...} @override void updated(InheritedWidget oldWidget) {...} @override void notifyClients(InheritedWidget oldWidget) {...} } 47

Slide 48

Slide 48 text

class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget super.widget); final Map _dependents = HashMap(); @override void _updateInheritance() {...} @override void debugDeactivated() {...} @protected Object? getDependencies(Element dependent) {...} @protected void updateDependencies(Element dependent, Object? aspect) {...} @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {...} @override void updated(InheritedWidget oldWidget) {...} @override void notifyClients(InheritedWidget oldWidget) {...} } 48 Log setState ParentWidget.build InheritedElement.updated InheritedWidget.updateShouldNotify:true InheritedWidget.updateShouldNotify:true InheritedElement.notifyClients InheritedElement.notifyDependent ChildWidget.didChangeDependencies ChildWidget.build InheritedElement.updateDependencies InheritedElement.setDependencies

Slide 49

Slide 49 text

49 ありがとうございました!

Slide 50

Slide 50 text

50 魂だけでも福岡に送ってみませんか? Discordサーバー