Un package de gestion d’état similaire à Android qui permet d’implémenter facilement le modèle MVVM.
Note: Dans Android, ViewModel a une fonctionnalité spéciale pour conserver l’état lors du changement de configuration. Le ViewModel de ce package n’est pas fait pour cela car Flutter Project n’en a pas besoin. C’est uniquement pour séparer la logique de vue de l’interface utilisateur.
🚀 DÉMO EN DIRECT D’UN EXEMPLE DE PROJET :
Caractéristiques
- Simplifié 😎 Gestion de l’état
- Modèle de code similaire avec un projet Android 🟰
- Facile pour les développeurs de migrer 🛩️ d’Android vers Flutter
- Permet au développeur de travailler facilement avec les projets Android et Flutter 🐥
- Modèle MVVM facile à mettre en œuvre 💪
Composants du package
- StateFlow, MutableStateFlow (équivalent à LiveData) ⛵
- SharedFlow, MutableSharedFlow 🌊
- ViewModel (pour séparer la logique de vue de l’interface utilisateur comme Cubit)
- ViewModelProvider
- StateFlowBuilder
- StateFlowConsumer
- StateFlowListenerStateFlowListenerStateFlowListener
- SharedFlowListener
- MultiFlowListener
- ChangeNotifierProvider
- Multifournisseur
Usage
my_view_model.dart
class CounterViewModel extends ViewModel {
// initialize MutableStateFlow with initial value 1
final _counterStateFlow = MutableStateFlow<int>(1);
StateFlow<int> get counterStateFlow => _counterStateFlow;
// you can also define more the one StateFlow or SharedFlow inside any ViewModel
void increment() {
// by changing the value, listeners notified
_counterStateFlow.value = _counterStateFlow.value + 1;
}
@override
void dispose() {
// must dispose all the StateFlows and SharedFlows
_counterStateFlow.dispose();
}
}
main.dart
void main() => runApp(CounterApp());
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ViewModelProvider(
create: (_) => CounterViewModel(),
child: CounterPage(),
),
);
}
}
counter_page.dart
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ViewModel Counter Example')),
body: Center(
// implement StateFlowBuilder to rebuild Text on StateFlow value changed/updated
child: StateFlowBuilder(
// pass your StateFlow
stateFlow: context.vm<CounterViewModel>().counterStateFlow,
builder: (context, value) {
return Text("$value", style: const TextStyle(fontSize: 30));
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// call the increment function which is inside MyViewModel
ViewModelProvider.of<CounterViewModel>(context).increment();
},
child: const Icon(Icons.add),
),
);
}
}
Composants du package
ViewModel (Créer une classe ViewModel personnalisée)
Créez votre View-Model personnalisé qui doit s’étendre ViewModel
. Déclarez tous vos flux et affichez la logique associée à l’intérieur. N’oubliez pas de disposer tous les flux à l’intérieur dispose
méthode de ViewModel
.
class CustomViewModel extends ViewModel {
// initialize StateFlow
final _myStateFlow = MutableStateFlow<int>(1);
StateFlow<int> get myStateFlow => _myStateFlow;
// view related logic here
@override
void dispose() {
// must dispose all flows
_myStateFlow.dispose();
}
}
PostFrameCallback avec ViewModel
Cela aidera à obtenir onPostFrameCallback
événement à l’intérieur ViewModel
facilement. En utilisant PostFrameCallback
on peut passer de :
WidgetsBinding.instance.addPostFrameCallback((_){
// do stuffs here
})
pour
class CustomViewModel extends ViewModel with PostFrameCallback {
//...
@override
void onPostFrameCallback(Duration timestamp) {
// do stuffs here
}
}
MutableStateFlow et StateFlow
MutableStateFlow
est hérité de StateFlow
. Il stocke la valeur et avertit les auditeurs chaque fois qu’il change. Il peut changer/mettre à jour la valeur.
Il est recommandé d’initialiser private
MutableStateFlow
et créer un publicStateFlow
acquéreur de celui-ci.
final _myStateFlow = MutableStateFlow<int>(1, notifyOnSameValue: true);
StateFlow<int> get myStateFlow => _myStateFlow;
Ici, notifyOnSameValue est facultatif. Si notifyOnSameValue
est défini sur faux, chaque fois que vous appelez stateFlow.value = newValue
où newValue est identique à la valeur actuelle, il n’informera pas les auditeurs. par défaut, il est défini sur vrai.
Pour changer la valeur
_myStateFlow.value = 5; // listeners were automatically notified
Pour mettre à jour la valeur
_listStateFlow.update((value) {
value.add(obj);
}); // listeners were automatically notified
MutableSharedFlow et SharedFlow
MutableSharedFlow
est hérité de SharedFlow
. Il est utilisé pour envoyer des données aux auditeurs. Il peut émettre la valeur.
Il est recommandé d’initialiser private
MutableSharedFlow
et créer un publicSharedFlow
acquéreur de celui-ci.
final _mySharedFlow = MutableSharedFlow<String>();
SharedFlow<int> get mySharedFlow => _mySharedFlow;
Pour émettre la valeur
_myStateFlow.emit("Hello from ViewModel!"); // listeners were automatically notified
Intégrer ViewModel dans le widget Flutter
ViewModelProvider
ViewModelProvider
est utilisé pour envelopper le widget avec votre personnalisé ViewModel
. Cela nécessite create
qui accepte la coutume ViewModel
et child
Widget.
ViewModelProvider(
create: (context) => counterViewModel, // provide your custom viewModel
child: ChildWidget(),
);
Obtenir l’instance ViewModel dans l’arborescence des widgets
ViewModelProvider.of<CustomViewModel>(context)
OU ALORS
context.vm<CustomViewModel>()
Widgets Builder, Listener et Consumer Flutter
StateFlowBuilder
StateFlowBuilder
est utilisé pour reconstruire les widgets à l’intérieur de celui-ci. Cela nécessite stateFlow
écouter et builder
à laquelle se reconstruit lorsque le stateFlow
La valeur de a été modifiée/mise à jour.
StateFlowBuilder(
stateFlow: context.vm<CustomViewModel>().myStateFlow, // pass StateFlow
builder: (context, value) {
return ChildWidget(value); // rebuild the widget with updated/changed value.
},
)
StateFlowConsumer
StateFlowConsumer
est utilisé pour reconstruire les widgets à l’intérieur et appeler l’écouteur. Cela nécessite stateFlow
à écouter, builder
et listener
. Chaque fois que stateFlow
la valeur de a été modifiée/mise à jour, builder
reconstruira les widgets à l’intérieur de celui-ci et listener
sera appelé.
StateFlowConsumer(
stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass SharedFlow
listener: (context, value) {
// do stuff here based on value
},
builder: (context, value) {
return ChildWidget(value); // rebuild the widget with updated/changed value.
},
)
StateFlowListenerStateFlowListenerStateFlowListener
StateFlowListener
est utilisé pour intercepter l’événement de modification/mise à jour de la valeur d’un stateFlow
. Cela nécessite stateFlow
, listener
et child
. Chaque fois que stateFlow
la valeur de a été modifiée/mise à jour, listener
sera appelé.
StateFlowListener(
stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass StateFlow
listener: (context, value) {
// do stuff here based on value
},
child: ChildWidget(),
)
SharedFlowListener
SharedFlowListener
est utilisé pour capter la valeur émise par sharedFlow
. Cela nécessite sharedFlow
, listener
et child
. Chaque fois que sharedFlow
émet une valeur, listener
sera appelé.
SharedFlowListener(
sharedFlow: ViewModelProvider.of<CustomViewModel>(context).mySharedFlow, // pass SharedFlow
listener: (context, value) {
// do stuff here based on value
},
child: ChildWidget(),
)
MultiFlowListener
MultiFlowListener
est un widget Flutter qui fusionne plusieurs SharedFlowListener
et StateFlowListener
widgets en un seul.
MultiFlowListener
améliore la lisibilité et élimine le besoin d’imbriquer plusieurs auditeurs. En utilisant MultiFlowListener
on peut passer de :
SharedFlowListener(
sharedFlow: context.vm<ViewModelA>().mySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
child: StateFlowListener(
stateFlow: context.vm<ViewModelA>().myStateFlow,
listener: (context, value) {
// do stuff here based on value
},
child: SharedFlowListener(
sharedFlow: context.vm<ViewModelB>().anySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
child: ChildA(),
)
)
)
pour
MultiFlowListener(
providers: [
SharedFlowListener(
sharedFlow: context.vm<ViewModelA>().mySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
),
StateFlowListener(
stateFlow: context.vm<ViewModelA>().myStateFlow,
listener: (context, value) {
// do stuff here based on value
},
),
SharedFlowListener(
sharedFlow: context.vm<ViewModelB>().anySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
),
],
child: ChildA(),
)
Fournisseur supplémentaire
Ce package inclut également certains des composants du fournisseur avec quelques modifications. Ceux-ci sont:
ChangeNotifierProvider
Cela permettra d’envelopper un widget autour de ChangeNotifier.
ChangeNotifierProvider(
create: (context) => CustomChangeNotifier(),
child: WidgetA(),
)
Pour obtenir l’instance de ChangeNotifier
ou écouter notifyListeners()
:
ChangeNotifierProvider.of<CustomChangeNotifier>(context, listen: true)
Si listen
est true
le widget se reconstruira sur notifyListeners()
. Cela peut aussi être écrit de manière simplifiée. Si vous voulez écouter notifyListeners()
utiliser:
context.watch<CustomChangeNotifier>()
Ou si vous ne voulez qu’une instance, utilisez :
context.read<CustomChangeNotifier>()
Note: Ici
context.watch
etcontext.read
est modifié à partir de la bibliothèque du fournisseur. Ici, le type est limité à ChangeNotifier.
Multifournisseur
MultiProvider
est un Widget qui fusionne plusieurs ViewModelProvider
et ChangeNotifierProvider
widgets en un seul.
MultiProvider
améliore la lisibilité et élimine le besoin d’imbriquer plusieurs widgets. En utilisant MultiProvider
on peut passer de :
ViewModelProvider(
create: (context) => ViewModelA(),
child: ViewModelProvider(
create: (context) => ViewModelB(),
child: ChangeNotifierProvider(
create: (context) => ChageNotifierA(),
child: ChildA(),
)
)
)
pour
MultiProvider(
providers: [
ViewModelProvider(
create: (context) => ViewModelA(),
),
ViewModelProvider(
create: (context) => ViewModelB(),
),
ChageNotifierProvider(
create: (context) => ChageNotifierA(),
),
],
child: ChildA(),
)
Note: Ce MultiProvider est différent de celui du package Provider. Cela n’acceptera que
ViewModelProvider
etChangeNotifierProvider
.
Contribuant
Les demandes d’extraction sont les bienvenues. Pour les modifications majeures, veuillez d’abord ouvrir un problème pour discuter de ce que vous souhaitez modifier.
GitHub
Voir Github