{"id":18737218,"url":"https://github.com/shubham16g/view_model_x","last_synced_at":"2025-06-13T20:36:26.691Z","repository":{"id":65190507,"uuid":"586584107","full_name":"shubham16g/view_model_x","owner":"shubham16g","description":"An Android similar state-management package (StateFlow and SharedFlow with ViewModel) which helps to implement MVVM pattern easily.","archived":false,"fork":false,"pushed_at":"2024-11-05T04:52:39.000Z","size":408,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-12T19:46:54.599Z","etag":null,"topics":["dart","dartlang","flutter","flutter-package","flutter-state-management","library","mvvm-pattern","sharedflow","state-management","stateflow","viewmodel-pattern"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/view_model_x","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shubham16g.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-08T16:45:12.000Z","updated_at":"2024-11-03T16:51:33.000Z","dependencies_parsed_at":"2024-09-15T03:03:52.397Z","dependency_job_id":"d908b403-4e8b-4656-be3a-7905a3c7beb2","html_url":"https://github.com/shubham16g/view_model_x","commit_stats":null,"previous_names":["shubham16g/view_model_x","shubham-gupta-16/view_model_x"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/shubham16g/view_model_x","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shubham16g%2Fview_model_x","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shubham16g%2Fview_model_x/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shubham16g%2Fview_model_x/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shubham16g%2Fview_model_x/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shubham16g","download_url":"https://codeload.github.com/shubham16g/view_model_x/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shubham16g%2Fview_model_x/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259717018,"owners_count":22900995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","dartlang","flutter","flutter-package","flutter-state-management","library","mvvm-pattern","sharedflow","state-management","stateflow","viewmodel-pattern"],"created_at":"2024-11-07T15:24:18.616Z","updated_at":"2025-06-13T20:36:26.652Z","avatar_url":"https://github.com/shubham16g.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/5dcaedbe-9871-40f1-aada-e1e2f6ae73d7\" height=\"120\" alt=\"Bloc\" /\u003e\n\u003c/p\u003e\n\u003ch1 align=\"center\"\u003e\n  View Model X\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e  \n  \u003ca href=\"https://pub.dev/packages/view_model_x\"\u003e\n    \u003cimg src=\"https://img.shields.io/pub/v/view_model_x.svg?color=success\u0026label=pub.dev\u0026logo=dart\u0026logoColor=0099ff\" alt=\"pub.dev\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nAn Android similar state management package which helps to implement MVVM pattern easily.\n\n## Why I made this package?\n\nIn Flutter, there are several state management options, each with its own advantages and\nlimitations:\n\n- **GetX:** GetX provides a powerful, unified approach with `GetXController`, enabling multiple\n  observables in a single file, which can simplify state management. However, GetX introduces its\n  own patterns for navigation, dependencies, and more, which can deviate from standard Flutter\n  conventions, leading to an ecosystem that doesn’t fully align with Flutter's core principles.\n\n- **Bloc/Provider (ChangeNotifier):** These are popular solutions that align well with Flutter’s\n  design philosophy. However, they often require creating multiple files to manage different states,\n  which can lead to boilerplate and reduced cohesiveness when managing related states.\n\nInspired by the **Android ViewModel**, I created a package that allows for multiple `StateFlows` and\n`SharedFlows` within a single `ViewModel`. This design combines the simplicity and cohesion of\nGetX’s GetXController with the modularity and adherence to Flutter’s patterns similar to `Provider`\nand `Bloc`. It allows for organized state management while following Flutter's native way of\nhandling widgets and reactivity.\n\n**🚀 LIVE DEMO OF EXAMPLE PROJECT:** https://shubham16g.github.io/view_model_x/\n\n## Features\n\n- Simplified 😎 State Management\n- Similar code pattern with an Android project 🟰\n- Easy for developers to migrate 🛩️ from Android to Flutter\n- Allows developer to work with both Android to Flutter projects with ease 🐥\n- Easy to implement MVVM pattern 💪\n\n## Package Components\n\n- StateFlow ⛵\n- SharedFlow 🌊\n- ViewModel (to separate the view logic from UI like Cubit/GetXController)\n- ViewModelProvider\n- StateFlowBuilder\n- StateFlowConsumer\n- StateFlowListener\n- SharedFlowListener\n- MultiFlowListener\n- MultiProvider\n\n## Usage\n\n### my_view_model.dart\n\n```dart\nclass CounterViewModel extends ViewModel {\n  // initialize StateFlow with initial value 1\n  final counterStateFlow = StateFlow\u003cint\u003e(1);\n\n  // you can also define more the one StateFlow or SharedFlow inside any ViewModel\n\n  void increment() {\n    // by changing the value, listeners notified\n    counterStateFlow.value = counterStateFlow.value + 1;\n  }\n\n  @override\n  void dispose() {\n    // must dispose all the StateFlows and SharedFlows\n    counterStateFlow.dispose();\n  }\n}\n```\n\n### main.dart\n\n```dart\nvoid main() =\u003e runApp(CounterApp());\n\nclass CounterApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      home: ViewModelProvider(\n        create: (_) =\u003e CounterViewModel(),\n        child: CounterPage(),\n      ),\n    );\n  }\n}\n```\n\n### counter_page.dart\n\n```dart\nclass CounterPage extends StatelessWidget {\n  const CounterPage({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(title: const Text('ViewModel Counter Example')),\n      body: Center(\n        // implement StateFlowBuilder to rebuild Text on StateFlow value changed/updated\n        child: StateFlowBuilder(\n            // pass your StateFlow\n            stateFlow: context.vm\u003cCounterViewModel\u003e().counterStateFlow,\n            builder: (context, value) {\n              return Text(\"$value\", style: const TextStyle(fontSize: 30));\n            },\n        ),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: () {\n          // call the increment function which is inside MyViewModel\n          ViewModelProvider.of\u003cCounterViewModel\u003e(context).increment();\n        },\n        child: const Icon(Icons.add),\n      ),\n    );\n  }\n}\n```\n\n## Package Components\n\n### ViewModel (Create custom ViewModel class)\nCreate your custom View-Model which must extends `ViewModel`. Declare all your Flows and View related logic inside of it.\nDon't forget to dispose all flows inside `dispose` method of `ViewModel`.\n\n```dart\nclass CustomViewModel extends ViewModel {\n  // initialize StateFlow\n  final myStateFlow = StateFlow\u003cint\u003e(1);\n\n  // view related logic here\n\n  @override\n  void dispose() {\n    // must dispose all flows\n    myStateFlow.dispose();\n  }\n}\n```\n\n### PostFrameCallback with ViewModel\nUsing `PostFrameCallback` with `ViewModel` helps to get `onPostFrameCallback` event inside `ViewModel` easily.\n\n```dart\nclass CustomViewModel extends ViewModel with PostFrameCallback {\n  //...\n  \n  @override\n  void onPostFrameCallback(Duration timestamp) {\n    // do stuffs here\n  }\n}\n```\n\n### StateFlow\nIt stores value and notify listeners whenever it changes. It can change/update the value.\n\n```dart\nfinal myStateFlow = StateFlow\u003cint\u003e(1, notifyOnSameValue: true);\n```\nOR\n```dart\nfinal myStateFlow = 1.stf();\n```\n\nHere, notifyOnSameValue is optional. If `notifyOnSameValue` is set to false, whenever you call `stateFlow.value = newValue`\nwhere newValue is same as current value, it will not notify listeners. by default it is set to true.\n\n**To change the value**\n\n```dart\nmyStateFlow.value = 5; // listeners were automatically notified\n```\n\n**To update the value**\nFor something like list or map, you may have to update the existing object instead on resigning a value.\n\n```dart\nlistStateFlow.update((value) {\n  value.add(obj);\n}); // listeners were automatically notified\n```\n\n### SharedFlow\nIt is used to send data to the listeners. It can emit the value.\n\n```dart\nfinal mySharedFlow = SharedFlow\u003cString\u003e();\n```\nOR\n```dart\nfinal mySharedFlow = stf\u003cString\u003e();\n```\n\n**To emit the value**\n\n```dart\nmyStateFlow.emit(\"Hello from ViewModel!\"); // listeners were automatically notified\n```\n\n## Integrate ViewModel Into Flutter Widget\n\n### ViewModelProvider\n\n`ViewModelProvider` is used to wrap the widget with your custom `ViewModel`.\nThis requires `create` which accepts custom `ViewModel` and `child` Widget.\n\n```dart\nViewModelProvider(\n  create: (context) =\u003e counterViewModel, // provide your custom viewModel\n  child: ChildWidget(),\n);\n```\n\n### Get ViewModel instance inside Widget Tree\n\n```dart\nViewModelProvider.of\u003cCustomViewModel\u003e(context)\n```\nOR\n```dart\ncontext.vm\u003cCustomViewModel\u003e()\n```\n\n## Builder, Listener, and Consumer Flutter Widgets\n\n### StateFlowBuilder\n\n`StateFlowBuilder` is used to rebuild the widgets inside of it.\nThis requires `stateFlow` to listen on and `builder` to which rebuilds when the `stateFlow`'s value changed/updated.\n\n```dart\nStateFlowBuilder(\n  stateFlow: context.vm\u003cCustomViewModel\u003e().myStateFlow, // pass StateFlow\n  builder: (context, value) {\n    return ChildWidget(value); // rebuild the widget with updated/changed value.\n  },\n)\n```\n\n\n### Binding with context `stateFlowObject.bind(BuildContext)`\nWe can use `bind` with any `StateFlow` which observe the value change and update the ui.\nThis is similar to Provider's `context.watch\u003cMyChangeNotifier\u003e()`.\n```dart\nfinal value = context.vm\u003cCustomViewModel\u003e().myStateFlow.bind(context);\n```\n\n### StateFlowConsumer\n\n`StateFlowConsumer` is used to rebuild the widgets inside of it and call the listener.\nThis requires `stateFlow` to listen on, `builder` and `listener`.\nWhenever `stateFlow`'s value changed/updated, `builder` will rebuild the widgets inside of it and `listener` will called.\n\n```dart\nStateFlowConsumer(\n  stateFlow: ViewModelProvider.of\u003cCustomViewModel\u003e(context).myStateFlow, // pass SharedFlow\n  listener: (context, value) {\n    // do stuff here based on value\n  },\n  builder: (context, value) {\n    return ChildWidget(value); // rebuild the widget with updated/changed value.\n  },\n)\n```\n\n### StateFlowListener\n\n`StateFlowListener` is used to catch the change/update value event of a `stateFlow`.\nThis requires `stateFlow`, `listener` and `child`.\nWhenever `stateFlow`'s value changed/updated, `listener` will called.\n\n```dart\nStateFlowListener(\n  stateFlow: ViewModelProvider.of\u003cCustomViewModel\u003e(context).myStateFlow, // pass StateFlow\n  listener: (context, value) {\n    // do stuff here based on value\n  },\n  child: ChildWidget(),\n)\n```\n\n\n### SharedFlowListener\n\n`SharedFlowListener` is used to catch the emitted value from `sharedFlow`.\nThis requires `sharedFlow`, `listener` and `child`.\nWhenever `sharedFlow` emits a value, `listener` will called.\n\n```dart\nSharedFlowListener(\n  sharedFlow: ViewModelProvider.of\u003cCustomViewModel\u003e(context).mySharedFlow, // pass SharedFlow\n  listener: (context, value) {\n    // do stuff here based on value\n  },\n  child: ChildWidget(),\n)\n```\n\n\n### MultiFlowListener\n\n`MultiFlowListener` is a Flutter widget that merges multiple `SharedFlowListener` and `StateFlowListener` widgets into one.\n`MultiFlowListener` improves the readability and eliminates the need to nest multiple listeners.\nBy using `MultiFlowListener` we can go from:\n\n```dart\nSharedFlowListener(\n  sharedFlow: context.vm\u003cViewModelA\u003e().mySharedFlow,\n  listener: (context, value) {\n    // do stuff here based on value\n  },\n  child: StateFlowListener(\n    stateFlow: context.vm\u003cViewModelA\u003e().myStateFlow,\n    listener: (context, value) {\n      // do stuff here based on value\n    },\n    child: SharedFlowListener(\n      sharedFlow: context.vm\u003cViewModelB\u003e().anySharedFlow,\n      listener: (context, value) {\n        // do stuff here based on value\n      },\n      child: ChildA(),\n    )\n  )\n)\n```\nto\n```dart\nMultiFlowListener(\n  providers: [\n    SharedFlowListener(\n      sharedFlow: context.vm\u003cViewModelA\u003e().mySharedFlow,\n      listener: (context, value) {\n        // do stuff here based on value\n      },\n    ),\n    StateFlowListener(\n      stateFlow: context.vm\u003cViewModelA\u003e().myStateFlow,\n      listener: (context, value) {\n        // do stuff here based on value\n      },\n    ),\n    SharedFlowListener(\n      sharedFlow: context.vm\u003cViewModelB\u003e().anySharedFlow,\n      listener: (context, value) {\n        // do stuff here based on value\n      },\n    ),\n  ],\n  child: ChildA(),\n)\n```\n\n### MultiProvider\n`MutliProvider` is `Provider` package's widget.\nIt merges multiple `ViewModelProvider`, `ChangeNotifierProvider`, and/or `Provider` widgets into one.\n`MultiProvider` improves the readability and eliminates the need to nest multiple widgets.\nBy using `MultiProvider` we can go from:\n\n```dart\nViewModelProvider(\n  create: (context) =\u003e ViewModelA(),\n  child: ViewModelProvider(\n    create: (context) =\u003e ViewModelB(),\n    child: ChangeNotifierProvider(\n      create: (context) =\u003e ChageNotifierA(),\n      child: ChildA(),\n    )\n  )\n)\n```\nto\n```dart\nMultiProvider(\n  providers: [\n    ViewModelProvider(\n      create: (context) =\u003e ViewModelA(),\n    ),\n    ViewModelProvider(\n      create: (context) =\u003e ViewModelB(),\n    ),\n    ChageNotifierProvider(\n      create: (context) =\u003e ChageNotifierA(),\n    ),\n  ],\n  child: ChildA(),\n)\n```\n\u003e **Note:** This MultiProvider is different from one in Provider package. This will only accepts `ViewModelProvider` and `ChangeNotifierProvider`.\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n\n\u003e **Note:** In Android, ViewModel have an special functionality of keeping the state on configuration change.\n\u003e The ViewModel of this package is not for that as Flutter Project doesn't need it. It is only for separating the View Logic from UI.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshubham16g%2Fview_model_x","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshubham16g%2Fview_model_x","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshubham16g%2Fview_model_x/lists"}