Skip to content

Commit

Permalink
Merge pull request #8
Browse files Browse the repository at this point in the history
Easy to implement ViewModel and example improved
  • Loading branch information
shubham16g authored Jan 14, 2023
2 parents ce11d78 + ed27a37 commit a8c8a16
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 26 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.5.1
Easy to implement ViewModel and example improved
- ViewModelStatelessWidget added
- PostFrameCallback example added
- viewModelStatelessWidget example added

## 0.5.0
Provider extras added (Major Changes)
- Provider package added
Expand Down
43 changes: 35 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ An Android similar state management package which helps to implement MVVM patter
- SharedFlow, MutableSharedFlow 🌊
- ViewModel (to separate the view logic from UI like Cubit)
- ViewModelProvider
- ViewModelStatelessWidget
- StateFlowBuilder
- StateFlowConsumer
- StateFlowListener
Expand Down Expand Up @@ -131,15 +132,8 @@ class CustomViewModel extends ViewModel {
```

### PostFrameCallback with ViewModel
This will help to get `onPostFrameCallback` event inside `ViewModel` easily.
By using `PostFrameCallback`, we can go from:
Using `PostFrameCallback` with `ViewModel` helps to get `onPostFrameCallback` event inside `ViewModel` easily.

```dart
WidgetsBinding.instance.addPostFrameCallback((_){
// do stuffs here
})
```
to
```dart
class CustomViewModel extends ViewModel with PostFrameCallback {
//...
Expand Down Expand Up @@ -219,6 +213,39 @@ OR
context.vm<CustomViewModel>()
```

### ViewModelStatelessWidget
**ViewModelStatelessWidget** helps to integrate `ViewModel` into `StatelessWidget` easily. Using this, we can go from:
```dart
class MyPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return ViewModelProvider(
create: (context) => MyViewModel(), // provide your custom viewModel
child: Scaffold(
// content here
),
);
}
}
```
to:
```dart
class MyPage extends ViewModelStatelessWidget<MyViewModel> {
const CounterPage({super.key});
@override
MyViewModel createViewModel(BuildContext context) => MyViewModel();
@override
Widget buildWithViewModel(BuildContext context, MyViewModel viewModel) {
return Scaffold(
// content here. For ViewModel instance, use `viewModel`
);
}
}
```
## Builder, Listener, and Consumer Flutter Widgets

### StateFlowBuilder
Expand Down
19 changes: 10 additions & 9 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,25 @@ class MyApp extends StatelessWidget {
useMaterial3: true,
primarySwatch: Colors.blue,
),
home: ViewModelProvider(
create: (context) => CounterViewModel(), child: const HomePage()),
home: const HomePage(),
);
}
}

class HomePage extends StatelessWidget {
class HomePage extends ViewModelStatelessWidget<CounterViewModel> {
const HomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
CounterViewModel createViewModel(BuildContext context) => CounterViewModel();

@override
Widget buildWithViewModel(BuildContext context, CounterViewModel viewModel) {
return Scaffold(
appBar: AppBar(title: const Text('ViewModel Example')),
// implement SharedFlowListener anywhere in code to listen for emits from sharedFlow
body: SharedFlowListener(
// pass your SharedFlow
sharedFlow: context.vm<CounterViewModel>().messageSharedFlow,
sharedFlow: viewModel.messageSharedFlow,
listener: (context, value) {
// get the emitted value. in this case <String>"Hello from ViewModel!"
ScaffoldMessenger.of(context)
Expand All @@ -82,7 +84,7 @@ class HomePage extends StatelessWidget {
// implement ViewModelBuilder to rebuild Text on StateFlow value changed/updated
child: StateFlowBuilder(
// pass your StateFlow
stateFlow: context.vm<CounterViewModel>().counterStateFlow,
stateFlow: viewModel.counterStateFlow,
builder: (context, value) {
return Text(
"$value",
Expand All @@ -101,16 +103,15 @@ class HomePage extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
onPressed: () {
// call the showPopupMessage function which is inside CounterViewModel
ViewModelProvider.of<CounterViewModel>(context)
.showPopupMessage();
viewModel.showPopupMessage();
},
icon: const Icon(Icons.mail_outline),
),
const SizedBox(width: 12),
FloatingActionButton(
onPressed: () {
// call the increment function which is inside CounterViewModel
ViewModelProvider.of<CounterViewModel>(context).increment();
viewModel.increment();
},
child: const Icon(Icons.add),
),
Expand Down
6 changes: 5 additions & 1 deletion example/lib/more_examples_section.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import 'package:example/multiple_view_models_example/multiple_view_models_example.dart';
import 'package:example/post_frame_callback_example/post_frame_callback_example.dart';
import 'package:example/view_model_stateless_widget_example/view_model_stateless_example.dart';
import 'package:flutter/material.dart';

final _moreExamples = {
"Multiple ViewModels Example": const MultipleViewModelsExample()
"Multiple ViewModels Example": const MultipleViewModelsExample(),
"PostFrameCallback Example": const PostFrameCallbackExample(),
"ViewModelStatelessWidget Example": const ViewModelStatelessWidgetExample(),
};

class MoreExamplesSection extends StatelessWidget {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:view_model_x/view_model_x.dart';

class SecondViewModel extends ViewModel with PostFrameCallback {
class SecondViewModel extends ViewModel {
// initialize SharedFlow
final _messageSharedFlow = MutableSharedFlow<String>();

Expand All @@ -18,11 +18,6 @@ class SecondViewModel extends ViewModel with PostFrameCallback {
debugPrint("init inside vm");
}

@override
void onPostFrameCallback(Duration timestamp) {
_messageSharedFlow.emit("onPostFrameCallback from SecondViewModel");
}

@override
void dispose() {
_messageSharedFlow.dispose();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:view_model_x/view_model_x.dart';

import 'view_model/my_view_model_with_post_frame_callback.dart';

class PostFrameCallbackExample extends StatelessWidget {
const PostFrameCallbackExample({super.key});

@override
Widget build(BuildContext context) {
return ViewModelProvider(
create: (context) => MyViewModelWithPostFrameCallback(),
// child: MaterialApp(home: const ContentPage()),
child: const ContentPage(),
);
}
}

class ContentPage extends StatelessWidget {
const ContentPage({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: const CloseButton(),
titleSpacing: 0,
title: const Text('ViewModel with PostFrameCallback Example')),
body: SharedFlowListener(
sharedFlow:
context.vm<MyViewModelWithPostFrameCallback>().messageSharedFlow,
listener: (context, value) {
// show SnackBar
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(value)));
},
child: const Center(
child: Text('SnackBar will appear on PostFrameCallback'),
),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:flutter/foundation.dart';
import 'package:view_model_x/view_model_x.dart';

class MyViewModelWithPostFrameCallback extends ViewModel
with PostFrameCallback {
// initialize SharedFlow
final _messageSharedFlow = MutableSharedFlow<String>();

SharedFlow<String> get messageSharedFlow => _messageSharedFlow;

@override
void init() {
// do stuff on create of view model, (equivalent to constructor)
debugPrint("on init");

_messageSharedFlow.emit(
"on init"); // this emit will not received by listener because it emitted before ui build.
}

@override
void onPostFrameCallback(Duration timestamp) {
_messageSharedFlow.emit(
"onPostFrameCallback"); // this emit will be received because it is emitted after the ui build completed.
}

@override
void dispose() {
_messageSharedFlow.dispose();
debugPrint("MyViewModelWithPostFrameCallback disposed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:view_model_x/view_model_x.dart';

class CounterViewModel extends ViewModel {
// initialize StateFlow
final _counterStateFlow = MutableStateFlow<int>(1);

StateFlow<int> get counterStateFlow => _counterStateFlow;

void increment() {
// by changing the value, listeners were notified
_counterStateFlow.value = _counterStateFlow.value + 1;
}

@override
void dispose() {
// must dispose all flows
_counterStateFlow.dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:view_model_x/view_model_x.dart';

import 'view_model/counter_view_model.dart';

class ViewModelStatelessWidgetExample
extends ViewModelStatelessWidget<CounterViewModel> {
const ViewModelStatelessWidgetExample({Key? key}) : super(key: key);

@override
CounterViewModel createViewModel(BuildContext context) => CounterViewModel();

@override
Widget buildWithViewModel(BuildContext context, CounterViewModel viewModel) {
return Scaffold(
appBar: AppBar(
leading: const CloseButton(),
titleSpacing: 0,
title: const Text('ViewModel Stateless Widget Example')),
// implement SharedFlowListener anywhere in code to listen for emits from sharedFlow
body: Center(
// implement ViewModelBuilder to rebuild Text on StateFlow value changed/updated
child: StateFlowBuilder(
// pass your StateFlow
stateFlow: viewModel.counterStateFlow,
builder: (context, value) {
return Text(
"$value",
style: const TextStyle(fontSize: 30),
);
}),
),
floatingActionButton: FloatingActionButton(
heroTag: "view_model_stateless_widget_example",
onPressed: () {
// call the increment function which is inside CounterViewModel
viewModel.increment();
},
child: const Icon(Icons.add),
),
);
}
}
6 changes: 5 additions & 1 deletion lib/src/view_model_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import 'view_model.dart';
class ViewModelProvider<T extends ViewModel> extends Provider<T>
with ProviderSingleChildWidget {
ViewModelProvider(
{super.key, required super.create, super.lazy, super.child});
{super.key,
required super.create,
super.lazy,
super.builder,
super.child});

/// [ViewModelProvider].[of] method allows to get the custom [ViewModel] from anywhere nested inside [ViewModelProvider]'s [child]
static F of<F extends ViewModel>(BuildContext context) {
Expand Down
21 changes: 21 additions & 0 deletions lib/src/view_model_stateless_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:flutter/widgets.dart';
import 'view_model_provider.dart';
import 'view_model.dart';

/// [ViewModelStatelessWidget] helps to integrate [ViewModel] into [StatelessWidget] easily.
abstract class ViewModelStatelessWidget<T extends ViewModel>
extends StatelessWidget {
const ViewModelStatelessWidget({Key? key}) : super(key: key);

T createViewModel(BuildContext context);

Widget buildWithViewModel(BuildContext context, T viewModel);

@override
Widget build(BuildContext context) {
return ViewModelProvider(
create: createViewModel,
builder: (context, w) => buildWithViewModel(context, context.vm<T>()),
);
}
}
1 change: 1 addition & 0 deletions lib/view_model_x.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export './src/state_flow_listener.dart';
export './src/shared_flow_listener.dart';
export './src/view_model_provider.dart';
export './src/multi_flow_listener.dart';
export './src/view_model_stateless_widget.dart';
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: view_model_x
description: An Android similar state management package (StateFlow and SharedFlow with ViewModel) which helps to implement MVVM pattern easily.
version: 0.5.0
version: 0.5.1
homepage: https://github.com/shubham-gupta-16/view_model_x
repository: https://github.com/shubham-gupta-16/view_model_x
issue_tracker: https://github.com/shubham-gupta-16/view_model_x/issues
Expand Down

0 comments on commit a8c8a16

Please sign in to comment.