Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実例と歴史から学ぶ_Flutterの状態管理方法の選定_.pdf

entaku
November 28, 2021

 実例と歴史から学ぶ_Flutterの状態管理方法の選定_.pdf

entaku

November 28, 2021
Tweet

More Decks by entaku

Other Decks in Technology

Transcript

  1. • Name: ԕ౻୓໻ (entaku) • Twitter: @entaku_0818 • Job: ϞόΠϧΞϓϦΤϯδχΞ

    • Android / Flutter / iOSͷܦݧ͋Γ • iOSΤϯδχΞͷܦݧ͕௕͍ ࣗݾ঺հ
  2. Flutterঢ়ଶ؅ཧͷͨ͘͞Μͷํ๏ StatefulWidget Provider Riverpod InheritedWidget ScopedModel Redux Fish-Redux BLoC /

    Rx GetIt https://docs. fl utter.dev/development/data-and-backend/state-mgmt ͷதͰ΋ಛʹຊےͷྲྀΕ͕෼͔Γͦ͏ͳͱ͜ΖΛϐοΫΞοϓ
  3. https://api. fl utter.dev/ fl utter/widgets/StatelessWidget-class.html https://api. fl utter.dev/ fl utter/widgets/StatefulWidget-class.html

    class GreenFrog extends StatelessWidget { const GreenFrog({ Key? key }) : super(key: key) ; @override Widget build(BuildContext context) { return Container(color: const Color(0xFF2DBD3A)) ; } } class YellowBird extends StatefulWidget { const YellowBird({ Key? key }) : super(key: key) ; @override State<YellowBird> createState() => _YellowBirdState() ; } class _YellowBirdState extends State<YellowBird> { @override Widget build(BuildContext context) { return Container(color: const Color(0xFFFFE306)) ; } } StatefulWidget - ެࣜͷࣄྫ
  4. class HomeScreen extends StatefulWidget { HomeScreen({Key key}) : super(key: key)

    ; ɹ// StateΛࢦఆ _HomeScreenState createState() => _HomeScreenState() ; } class _HomeScreenState extends State<HomeScreen> { ɹ ɹ// νʔϜҰཡΛఆ͍ٛͯ͠Δ List<Team> _teams = <Team>[] ; void initState() { // StateͷॳظԽॲཧ super.initState() ; fetch(); // ը໘ΞΫηε࣌ʹσʔλΛऔಘͯ͘͠Δ } StatefulWidget - SampleCode
  5. @overrid e Widget build(BuildContext context) { return Scaffold ( appBar:

    AppBar ( title: const Text('νʔϜҰཡ') , ) , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(_teams[index].name) , ) , ) ; } , itemCount: _teams.length , ) , ) ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { setState(() { _teams = teams ; }) ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } StatefulWidget - SampleCode
  6. // Note: It must extend from Model. class CounterModel extends

    Model { int _counter = 0 ; int get counter => _counter ; void increment() { // First, increment the counter _counter++ ; // Then notify all the listeners. notifyListeners() ; } } ScopedModel - ެࣜͷࣄྫ ঢ়ଶΛ΋ͭ.PEFMΛఆٛ
  7. class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) {

    // First, create a `ScopedModel` widget. This will provide // the `model` to the children that request it. return new ScopedModel<CounterModel> ( model: new CounterModel() , child: new Column(children: [ // Create a ScopedModelDescendant. This widget will get the // CounterModel from the nearest ScopedModel<CounterModel>. // It will hand that model to our builder method, and rebuild // any time the CounterModel changes (i.e. after we // `notifyListeners` in the Model). new ScopedModelDescendant<CounterModel> ( builder: (context, child, model) => new Text('${model.counter}') , ) , new Text("Another widget that doesn't depend on the CounterModel" ) ] ) ) ; } } ScopedModel - ެࣜͷࣄྫ ར༻͢Δ 4DPQFE.PEFMΛఆٛ .PEFMΛར༻͍ͨ͠8JEHFUΛ 4DPQFE.PEFM%FTDFOEBOUͰ แΉ
  8. class ScopedModelHome extends StatelessWidget { @overrid e Widget build(BuildContext context)

    { // StatelessWidgetͰఆٛ͠ModelΛݺͼग़͢=> ViewʹϏδωεϩδοΫ͕ೖͬͯ͜ͳ͍ return ScopedModel<TeamScopedModel> ( model: TeamScopedModel() , child: ScopedModelDescendant<TeamScopedModel> ( // ScopedModelDescendant഑ԼͰϞσϧΛࢀর builder: (context, child, model) => Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(model.teams[index].name) , ) , ) ; } , itemCount: model.teams.length , ) , ) , ) , ) ; } } ScopedModel - SampleCode
  9. class TeamScopedModel extends Model { List<Team> _teams = [] ;

    List<Team> get teams => _teams ; TeamScopedModel() { // ॳճ࣮ߦ࣌ʹσʔλऔಘΛ࣮ߦ fetch() ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { _teams = teams ; // σʔλߋ৽࣌ʹΠϕϯτൃՐ notifyListeners() ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } } ScopedModel - SampleCode
  10. ϝϦοτ σϝϦοτ w ࠷ޙͷߋ৽͕ w ·ͩ/VMM4BGFUZʹ΋ରԠ͍ͯ͠ͳ͍ w ࠓޙߋ৽͞ΕΔՄೳੑ͸௿͍ʁ w w

    4UBUFGVM8JEHFUͱൺֱͯ͠ϏδωεϩδοΫΛ෼཭͠΍͍͢ ScopedModel - SampleCode
  11. void main() { runApp ( ChangeNoti fi erProvider ( create:

    (context) => CartModel(), // ChangeNoti fi e r child: const MyApp(), // Widge t ) , ) ; } Provider - ެࣜͷࣄྫ $IBOHF/PUJ fi FS1SPWJEFS Ͱ$IBOHF/PUJ fi FSͱ8JEHFUΛ ఆٛ
  12. class CartModel extends ChangeNoti fi er { /// Internal, private

    state of the cart. fi nal List<Item> _items = [] ; /// An unmodi fi able view of the items in the cart. Unmodi fi ableListView<Item> get items => Unmodi fi ableListView(_items) ; /// The current total price of all items (assuming all items cost $42). int get totalPrice => _items.length * 42 ; /// Adds [item] to cart. This and [removeAll] are the only ways to modify the /// cart from the outside. void add(Item item) { _items.add(item) ; // This call tells the widgets that are listening to this model to rebuild. notifyListeners() ; } /// Removes all items from the cart. void removeAll() { _items.clear() ; // This call tells the widgets that are listening to this model to rebuild. notifyListeners() ; } } Provider - ެࣜͷࣄྫ $IBOHF/PUJ fi FSͰ஋Λఆٛ
  13. return Consumer<CartModel> ( builder: (context, cart, child) { return Text("Total

    price: ${cart.totalPrice}") ; } , ); Provider - ެࣜͷࣄྫ $POTVNFSͰғ͏͜ͱͰ $IBOHF/PUJ fi FSൃՐ࣌ʹ 8JEHFUΛߋ৽
  14. Provider - SampleCode '/changeNotifier': (BuildContext context) => ChangeNotifierProvider ( create:

    (context) => TeamModel(),ɹ// ChangeNotifie r child: ChangeNotifierHome(), // Widge t ) ,
  15. Provider - SampleCode class ChangeNotifierHome extends StatelessWidget { @overrid e

    Widget build(BuildContext context) { return Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , // Widget಺ͷߋ৽ൣғΛConsumerͰғ͏ body: Consumer<TeamModel> ( builder: (BuildContext context, TeamModel value, Widget child) { return ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(value.teams[index].name) , ) , ) ; } , itemCount: value.teams.length , ) ; })) ; } }
  16. Provider - SampleCode class TeamModel extends ChangeNotifier { List<Team> _teams

    = [] ; List<Team> get teams => _teams ; TeamModel() { // ॳظԽ࣌ʹσʔλऔಘ fetch() ; } Future<List<Team>> fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { _teams = teams ; //ɹมߋΛ௨஌ notifyListeners() ; } , failure: (error) { //ɹΤϥʔॲཧ } , ) ; } }
  17. fi nal counterProvider = StateNoti fi erProvider((ref) { return Counter()

    ; }) ; class Counter extends StateNoti fi er<int> { Counter(): super(0) ; void increment() => state++ ; } Riverpod - ެࣜͷࣄྫ άϩʔόϧʹ1SPWJEFSΛఆٛ
  18. class Example extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef

    ref) { fi nal count = ref.watch(counterProvider) ; return Text(count.toString()) ; } } Riverpod - ެࣜͷࣄྫ $POTVNFS8JEHFUͰ8JEHFUΛੜ੒ ͠ɺ SFGXBUDIͰ1SPWJEFSͷঢ়ଶΛ؅ཧ
  19. Riverpod // TeamStateNotifierΛࢀর͠ɺάϩʔόϧʹެ։ final TeamStateNotifierProvider = StateNotifierProvider<TeamStateNotifier, List<Team>> ( (ref)

    => TeamStateNotifier([]) , ) ; class TeamStateNotifier extends StateNotifier<List<Team>> { TeamStateNotifier(List<Team> state) : super(state) { // ॳظԽ࣌ʹσʔλऔಘ _fetch() ; } Future<List<Team>> _fetch() async { final _teamsRepository = TeamsRepository() ; final result = await _teamsRepository.feachTeams() ; result.when ( success: (teams) { state = teams ; } , failure: (error) { // Τϥʔॲཧ } , ) ; } }
  20. Riverpod class RiverpodHome extends ConsumerWidget { @overrid e Widget build(BuildContext

    context, WidgetRef ref) { // ref.watchͰProviderͷঢ়ଶΛࢀর final teams = ref.watch(TeamStateNotifierProvider) ; return Scaffold ( appBar: AppBar ( title: const Text('νʔϜҰཡ') , ) , drawer: Header() , body: ListView.builder ( itemBuilder: (BuildContext context, int index) { return Card ( child: ListTile ( title: Text(teams[index].name) , ) , ) ; } , itemCount: teams.length , ) , ) ; } }
  21. ϗϯτͷ·ͱΊ • ·ͣγϯϓϧͳΞϓϦΛ࡞ΔͳΒStatefulWidget͕͍͍ • ܦݧऀ΋ଟ͍ͷͰ࣮ྫ΋͋Δ͸ͣ • ScopedModel͸ߋ৽͞Εͯͳ͍Α͏ͩ͠ࠓޙ͸࢖͏΂͖Ͱ͸ͳ ͦ͞͏ • ෳ਺ը໘Ͱঢ়ଶΛڞ༗͢Δ৔߹͸Provider΍RiverpodΛ࢖༻͢Δ

    • ҆ఆੑ͕ཉ͍͠ͳΒProvider • ը໘ભҠ͕ෳࡶͳΒRiverpod • (iOS։ൃͷࣄྫ͔Β)ϚϧνϞδϡʔϧԽͯ͠ϛχΞϓϦΛ࡞Δ͜ͱ ͕ٻΊΒΕΔΑ͏ʹͳΔՄೳੑ
  22. ࢀߟ • Flutter SDKެࣜαΠτ- https://api. fl utter.dev/ • Flutter Developer

    ެࣜυΩϡϝϯτ - https:// fl utter.dev/development • iOSΞϓϦઃܭύλʔϯೖ໳ - https://peaks.cc/books/iOS_architecture • iOSDC ػೳ͝ͱʹಈ࡞͢ΔϛχΞϓϦͰϓϨϏϡʔαΠΫϧΛര଎ʹͨ͠࿩ - https://speakerdeck.com/aomathwift/ji-neng-gotonidong-zuo- suruminiapuridepurebiyusaikuruwobao-su-nisitahua • Flutterͷঢ়ଶ؅ཧख๏ͷબఆ - https://medium.com/ fl utter-jp/ state-1daa7fd66b94 • ίʔυੜ੒Λ༻͍ͨiOSΞϓϦϚϧνϞδϡʔϧԽͷͨΊͷґଘղܾ - https://techlife.cookpad.com/entry/2021/06/16/110000 • Flutter Ͱ࡞ΒΕͨ༑ਓͷΞϓϦ - ⭐ https://github.com/bannzai/Pilll ⭐