Skip to content

Instantly share code, notes, and snippets.

@sheerazam
Last active January 7, 2025 13:07
Show Gist options
  • Save sheerazam/0b52f51633f994941593627ee4405206 to your computer and use it in GitHub Desktop.
Save sheerazam/0b52f51633f994941593627ee4405206 to your computer and use it in GitHub Desktop.
Flutter MVI
/// Try running this code using DartPad: https://dartpad.dev/531149f182c1c2e340652281e4ca01ec
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// Model
class AppState {
final int counter;
final bool isLoading;
final String? error;
AppState({
required this.counter,
this.isLoading = false,
this.error,
});
AppState copyWith({
int? counter,
bool? isLoading,
String? error,
}) {
return AppState(
counter: counter ?? this.counter,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
}
/// View Model
class AppViewModel extends ChangeNotifier {
AppState _state = AppState(counter: 0);
AppState get state => _state;
IncrementCounterUseCase incUseCase = IncrementCounterUseCase();
DecrementCounterUseCase decUseCase = DecrementCounterUseCase();
void incrementCounter() {
var updatedCounter = incUseCase.updateCounter(_state.counter);
_state = _state.copyWith(counter: updatedCounter);
notifyListeners();
}
void decrementCounter() {
var updatedCounter = decUseCase.updateCounter(_state.counter);
_state = _state.copyWith(counter: updatedCounter);
notifyListeners();
}
}
class IncrementCounterUseCase {
int updateCounter(int counter) {
return counter + 1;
}
}
class DecrementCounterUseCase {
int updateCounter(int counter) {
return counter - 1;
}
}
/// View
class AppView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter MVI Example'),
),
body: Center(
// Note: Consumer widget of Provider State Management
child: Consumer<AppViewModel>(
builder: (context, viewModel, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (viewModel.state.isLoading)
CircularProgressIndicator()
else if (viewModel.state.error != null)
Text('Error: ${viewModel.state.error}')
else
Text(
'Counter: ${viewModel.state.counter}',
style: TextStyle(fontSize: 24),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => viewModel.incrementCounter(),
child: Text('Increment'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () => viewModel.decrementCounter(),
child: Text('Decrement'),
),
],
),
],
);
},
),
),
);
}
}
/// main function
void main() {
runApp(
// Note: Provider widget of Provider State Management
ChangeNotifierProvider(
create: (context) => AppViewModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AppView(),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment