stacked-org / stacked Goto Github PK
View Code? Open in Web Editor NEWA Flutter application architecture created from real world scenarios
License: MIT License
A Flutter application architecture created from real world scenarios
License: MIT License
When you see something similar to below you're most likely not using the stable branch of flutter. If that's the case you'll have to override the get package version to point to the dev-master version. It's the version on the right side of the two versions here
In your pubspec.yaml file add
dependency_overrides:
get: '2.0.9-dev'
Excpected or similar output below. Anything with get in the paths provided.
Compiler message:
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:279:8: Error: The method 'SnackRoute.install' has more required arguments than those of overridden method 'OverlayRoute.install'.
void install(OverlayEntry insertionPoint) {
^
/C:/flutter/packages/flutter/lib/src/widgets/routes.dart:41:8: Context: This is the overridden method ('install').
void install() {
^
Compiler message:
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:279:8: Error: The method 'SnackRoute.install' has more required arguments than those of overridden method 'OverlayRoute.install'.
void install(OverlayEntry insertionPoint) {
^
/C:/flutter/packages/flutter/lib/src/widgets/routes.dart:41:8: Context: This is the overridden method ('install').
void install() {
^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/routes/default_route.dart:242:9: Error: No named parameter with the name 'animation'.
animation: animation,
^^^^^^^^^
/C:/flutter/packages/flutter/lib/src/cupertino/route.dart:436:3: Context: Found this candidate, but the arguments don't match.
CupertinoFullscreenDialogTransition({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:289:18: Error: Too many positional arguments: 0 allowed, but 1 found.
Try removing the extra positional arguments.
super.install(insertionPoint);
^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/routes/default_route.dart:242:9: Error: No named parameter with the name 'animation'.
animation: animation,
^^^^^^^^^
/C:/flutter/packages/flutter/lib/src/cupertino/route.dart:436:3: Context: Found this candidate, but the arguments don't match.
CupertinoFullscreenDialogTransition({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:289:18: Error: Too many positional arguments: 0 allowed, but 1 found.
Try removing the extra positional arguments.
super.install(insertionPoint);
^
Target kernel_snapshot failed: Exception: Errors during snapshot creation: null
build failed.
Hi,
There's a typo on one of the NavigationService functions:
/// Pops the back stack until the predicate is satisfied
void popUnitl(RoutePredicate predicate) { // popUntil instead of popUnitl
Get.key.currentState.popUntil(predicate);
}
Regards,
Hi Team,
Firstly, thanks for the library and the youtube channel.
I have been using the provider model and a variation of your initial framework for a couple of months and I am in the process of migrating to Stacked.
A common scenario we seem to experience is the following -
The issue we are having is that we get an exception complaining about the ViewModel being disposed and that we should not call notify listeners.
I have solved this issue using the following code which is basically my own class that extends your BaseViewModel -
`
class BaseViewModelExtended extends BaseViewModel {
bool _disposed = false;
bool get disposed => _disposed;
void notifyListeners() {
safeNotifyListeners();
}
void safeNotifyListeners() {
if (!disposed) super.notifyListeners();
}
@OverRide
void dispose() {
super.dispose();
_disposed = true;
}
}
`
I am happy to know if I am missing something so I can correct the code. Otherwise, could you consider adding something similar to stacked?
Regards,
Tarek
Can't seem to get this working...
Amends....
locator.icondig.dart (although it says not to amend as generated....)
g.registerLazySingleton<DialogService>(() => DialogService());
home_viewmodel.dart
import 'package:new_architecture/app/locator.dart';
import 'package:new_architecture/services/dialog_service.dart';
final DialogService _dialogService = locator<DialogService>();
Future showD() async{
await _dialogService.showDialog(
title: 'A Dialog',
description: 'General Dialogue',
);
}
home_view.dart
floatingActionButton: FloatingActionButton(
onPressed: () {
// Navigator.of(context).push(MaterialPageRoute(
// builder: (context) => StartupView(),
// ));
model.showD();
},
),
but errors with
Restarted application in 1,911ms.
I/flutter (17729): HomeViewModel created
I/flutter (17729): HomeViewModel initialise
E/flutter (17729): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method 'dependOnInheritedWidgetOfExactType' was called on null.
E/flutter (17729): Receiver: null
E/flutter (17729): Tried calling: dependOnInheritedWidgetOfExactType<_InheritedTheme>()
�[38;5;244mE/flutter (17729): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)�[39;49m
�[38;5;244mE/flutter (17729): #1 Theme.of�[39;49m
�[38;5;248mE/flutter (17729): #2 Get.dialog�[39;49m
�[38;5;248mE/flutter (17729): #3 DialogService.showDialog�[39;49m
�[38;5;248mE/flutter (17729): #4 HomeViewModel.showD�[39;49m
�[38;5;248mE/flutter (17729): #5 HomeView.build.<anonymous closure>.<anonymous closure>�[39;49m
�[38;5;244mE/flutter (17729): #6 _InkResponseState._handleTap�[39;49m
�[38;5;244mE/flutter (17729): #7 _InkResponseState.build.<anonymous closure>�[39;49m
�[38;5;244mE/flutter (17729): #8 GestureRecognizer.invokeCallback�[39;49m
�[38;5;244mE/flutter (17729): #9 TapGestureRecognizer.handleTapUp�[39;49m
�[38;5;244mE/flutter (17729): #10 BaseTapGestureRecognizer._checkUp�[39;49m
�[38;5;244mE/flutter (17729): #11 BaseTapGestureRecognizer.handlePrimaryPointer�[39;49m
�[38;5;244mE/flutter (17729): #12 PrimaryPointerGestureRecognizer.handleEvent�[39;49m
�[38;5;244mE/flutter (17729): #13 PointerRouter._dispatch�[39;49m
�[38;5;244mE/flutter (17729): #14 PointerRouter._dispatchEventToRoutes.<anonymous closure>�[39;49m
�[38;5;244mE/flutter (17729): #15 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)�[39;49m
�[38;5;244mE/flutter (17729): #16 PointerRouter._dispatchEventToRoutes�[39;49m
�[38;5;244mE/flutter (17729): #17 PointerRouter.route�[39;49m
�[38;5;244mE/flutter (17729): #18 GestureBinding.handleEvent�[39;49m
�[38;5;244mE/flutter (17729): #19 GestureBinding.dispatchEvent�[39;49m
�[38;5;244mE/flutter (17729): #20 GestureBinding._handlePointerEvent�[39;49m
�[38;5;244mE/flutter (17729): #21 GestureBinding._flushPointerEventQueue�[39;49m
�[38;5;244mE/flutter (17729): #22 GestureBinding._handlePointerDataPacket�[39;49m
�[38;5;244mE/flutter (17729): #23 _rootRunUnary (dart:async/zone.dart:1138:13)�[39;49m
�[38;5;244mE/flutter (17729): #24 _CustomZone.runUnary (dart:async/zone.dart:1031:19)�[39;49m
�[38;5;244mE/flutter (17729): #25 _CustomZone.runUnaryGuarded (dart:async/zone.dart:933:7)�[39;49m
�[38;5;244mE/flutter (17729): #26 _invoke1 (dart:ui/hooks.dart:273:10)�[39;49m
�[38;5;244mE/flutter (17729): #27 _dispatchPointerDataPacket (dart:ui/hooks.dart:182:5)�[39;49m
E/flutter (17729):
Referring back to provider_architecture approach and the dialog_service there is different with a dialog_manager? So can't work out what might need amending...
Cheers.
PS also had to be on Stable flutter Channel for it to work with get package without changing get version.
dependencies:
get: ^1.17.3 // ^1.20.1-dev on beta/dev/master
At the outset, big thanks for sharing the library which helped me to simplify the state management. I wonder whether I am following the right way to rebuild FutureViewModel.
My initial view has a text field (for the user to enter search string) and a text that represent the search result.
class SearchFutureView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<SearchFutureViewModel>.reactive(
builder: (context, viewModel, child) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
_StringForm(),
FutureExampleView(
searchString: viewModel.searchText ?? 'Yet to initiate the search',
),
],
),
),
);
},
viewModelBuilder: () => SearchFutureViewModel(),
);
}
}
class _StringForm extends HookViewModelWidget<SearchFutureViewModel> {
_StringForm({Key key}) : super(key: key, reactive: false);
@override
Widget buildViewModelWidget(BuildContext context, SearchFutureViewModel viewModel) {
var text = useTextEditingController();
return TextField(
controller: text,
onSubmitted: (value) {
viewModel.updateSearchText(value);
}
);
}
}
class SearchFutureViewModel extends BaseViewModel {
String _searchText;
String get searchText => _searchText;
void updateSearchText(String value) {
_searchText = value;
notifyListeners();
}
}
Modified your example on future to accept the searchString
class FutureExampleView extends StatelessWidget {
final String searchString;
const FutureExampleView({Key key, this.searchString}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<FutureExampleViewModel>.reactive(
builder: (context, viewModel, child) {
return Expanded(
child: viewModel.hasError
? Container(color: Colors.red)
: Center(
child: viewModel.isBusy ? CircularProgressIndicator() : Text(viewModel.data),
),
);
},
viewModelBuilder: () => FutureExampleViewModel(searchString),
);
}
}
class FutureExampleViewModel extends FutureViewModel<String> {
final _dialogService = locator<DialogService>();
final String sampleData ;
FutureExampleViewModel(this.sampleData);
Future<String> getDataFromServer() async {
await Future.delayed(Duration(seconds: 3));
return sampleData;
}
@override
Future<String> futureToRun() => getDataFromServer();
@override
void onError(error) {
_dialogService.showDialog(
title: 'Error with Future',
description: error.toString(),
);
super.onError(error);
}
@override
void dispose() {
print('FutureExampleViewModel is getting disposed');
super.dispose();
}
}
Upon entering text in the text field, build
method of FutureExampleView
is getting called. However FutureExampleViewModel
is not getting called. Can FutureExampleView
be called in SearchFutureView
should rebuild the FutureExampleViewModel
.
Other approach is to call runFuture()
.
Thanks for looking into this.
Imagine an extension to Widget that returns another widget that rebuilds based on a stream (it's like just a wrapper for StreamBuilder) but no need to have all the boilerplate in the UI, something like this:
Text('${model.title.value}')
.Bind(model.title)In this case, model.title is a BindableProperty and calling .value on it will return the current value of it.
BindableProperty is an object that holds a value and whenever you set it will trigger rebuild on the binded widgets (behind the scenes it simply wraps a stream controller) and as mentioned, the .Bind wraps the widget in a StreamBuilder but I'm pretty sure this is where it stops being possible, maybe something different that by exploiting the extension methods we don't need so much boilerplate? I don't know, it's just an idea :)
Comment from Part 1 of the series with a great idea for boilerplate-less binding. Replace BindableProperty with RxValue from Observable-ish and we might have a pretty dope solution on our hands.
I haven'te technically vetted this but I like the idea.
Hi. Absolutely loving the latest update but have a question regarding StreamViewModel.
Please could you advise how I can listen to a stream based on a provided parameter? It appears the get stream is called before providing any opportunity to pass in a parameter. I have tried the constructor to no avail.
return ViewModelBuilder<EventViewModel>.reactive( disposeViewModel: false, viewModelBuilder: () => EventViewModel(_eventId), builder: (context, model, child) =>
@override Stream<List<EventCategory>> get stream => _database.eventsCategory(_eventId);
Thank you.
Hi, I'm Trying to implement a very basic app, but it doesn’t carry over the state (from portrait to landscape) or vice-versa , Or there's something I'm missing
My snip code :
HomeView
class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<HomeViewModel>.reactive(
viewModelBuilder: () => HomeViewModel(),
onModelReady: (model) => model.initialise(),
builder: (context, model, child) => ScreenTypeLayout.builder(
mobile: (context) => OrientationLayoutBuilder(
portrait: (context) => HomeMobilePortrait(),
landscape: (context) => HomeMobileLandscape(),
),
),
);
}
}
Portrait
class HomeMobilePortrait extends ViewModelWidget<HomeViewModel> {
@override
Widget build(BuildContext context, HomeViewModel model) {
return Scaffold(
body: Container(
child: Text(model.title),
alignment: Alignment.center,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => model.updateTitle(),
),
);
}
}
Landscape
class HomeMobileLandscape extends ViewModelWidget<HomeViewModel> {
@override
Widget build(BuildContext context, HomeViewModel model) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: Text(
model.title,
style: TextStyle(color: Colors.purple),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => model.updateTitle(),
),
);
}
}
Okay, first of all, great package! And I love your architecture design principle.
I followed your tutorial in YouTube, and I am having trouble implementing NavigationService, and I keep getting this error:
The getter '_key' was called on null.
Receiver: null
Tried calling: _key
The relevant error-causing widget was:
MyApp file:///D:/Documents/AndroidStudioProjects/testing_app/lib/main.dart
It's pointing me to NavigationService.navigatorKey. Do you have any idea what I might have been missing?
IMO, 3rd party integrations like flutter_hooks should be optional and be provided via different packages. Otherwise the dependency stack of the core package can be huge and hard to maintain.
I'm trying to navigate to next view by clearing all the view from the stack and passing a single argument but not able to capture in the next view.
Code for navigating to next page
locator<NavigationService>().clearStackAndShow(
Routes.profileViewRoute,
arguments: ProfileViewArguments(fromLoginView: true));
Constructor on Profile View
ProfileView({this.fromLoginView});
router.g.dart Code for Profile View
case Routes.profileViewRoute:
if (hasInvalidArgs<ProfileViewArguments>(args)) {
return misTypedArgsRoute<ProfileViewArguments>(args);
}
final typedArgs =
args as ProfileViewArguments ?? ProfileViewArguments();
return MaterialPageRoute<dynamic>(
builder: (context) =>
ProfileView(fromLoginView: typedArgs.fromLoginView),
settings: settings,
);
Expected value on ProfileView of fromLoginView is true but when it is passed via clearStackAndShow() it get false
Note: It work with clearTillFirstAndShow() but not with clearStackAndShow()
Could you please provide a simple use case on how to use the manager part of this architecture?
Here's how I understand it:
Regards,
Hi. I seem to have hit a problem with the StreamViewModel between the 1.2.4 and 1.3.0 update.
Quite simply I have a simple widget initiated like the usual from the docs ViewModelBuilder.reactive.
On my ViewModel I know I get data back within:
@override void onData(List<Offer> data) { print(data.length.toString()); }
Within my widget I am simply doing this:
child: model.data != null ? Container( etc etc etc) : SizedBox()
But the widget is not rebuilt with the data. This was and still working perfectly in 1.2.4. Am I missing something here which I have not interpreted from the docs?
Thank you
i have noticed that there is no option to add a custom UI for dialogservice, hope the feature is in development.
Hello Dane,
I am still trying to discover stacked and how to implement it in a full app.
I have quite easily transposed screens into views/viewmodel, and api into services. Thanks again for the tip about AutomaticKeepAliveClientMixin.
But now, I can't figure out how a view can influence another view.
For exemple :
If I have a MainView/MainViewModel : a Scaffold with some infos, a DrawerView/DrawerViewModel and a button to log in ...
The Login screen is a LoginView (stacked) with a LoginViewModel.
One the login is successful, how to update the Drawer (to see more options) and the MainView (change the log in button to a logout button)..
Thanks for your advice
Vincent
Currently the streamsSubscriptions
map has @visibleForTesting
attached to it, which doesn't let me conditionally pause a stream.
i.e.- I want to add this to stream1.onData:
if(condition){
streamsSubscriptions['timer'].resume
}else{
streamsSubscriptions['timer'].pause;
}
If there's a way to do this another way let me know.
Hello,
I am new in Flutter and I was looking for an architecture model.
After having seen your tutorial, I have decided to try to implement the Stacked philosophy.
I use some training codes I made to implement Stacked. Everything is fine with the View and Viewmodel.
In one code I use, there is a Scaffold and a bottomNavigationBar with 4 pages.
Each page has a list, and to preserve the scroll position, they implement : AutomaticKeepAliveClientMixin
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text("Home"),
subtitle: Text("$index"),
);
},
);
}
}
The tutorial is : https://cantaspinar.com/persistent-bottom-navigation-bar-in-flutter/
How implement AutomaticKeepAliveClientMixin using Stacked? I have tried with
... extends ViewModelBuilderWidget with AutomaticKeepAliveClientMixin
but I have an error
Thanks for your help
Followed documentation not sure what could be causing this.
setState() or markNeedsBuild() called during build.
This _DefaultInheritedProviderScope<ProfileViewModel> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
Why be there no const
constructors for the immutable ViewModelWidget
class?
Should actually look like this:
const ViewModelWidget({Key key, this.reactive = true}) : super(key: key);
What is the rationale behind that decision?
I'm trying to implement the equivalent of Navigator.pop(context)
in this architecture's Navigation Service.
the scenario is i have transitioned from ViewA to ViewB and that works fine... the transition is great ...
In ViewB i have a button to go back ... and in my onTapped I call a function on the model something like Future navigateBack() async {}
...
In this function if i do ... await _navigationService.navigateTo(Routes.viewA);
... it works but the transition is not great and looks wonky.
if I do await _navigationService.navigateWithTransition(HomeView(), transition: "leftToRight",);
it works, the transition is better but not really the same as if I had to do Navigator.pop(context)
is there another call that i need to use?
Also note in the video you emulate on Android, which has a hardware back button which we don't have on iOS...
The transition i am after on iOS is the same as if I had to swipe the page to the right... which behaves like Navigator.pop(context);
Attempting to navigate to next page using locator<NavigationService>().clearTillFirstAndShow(Routes.appBaseViewRoute);
but it throws error on popUntil()
Error
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '(dynamic) => dynamic' is not a subtype of type '(Route<dynamic>) => bool' of 'predicate'
Hi,
This query is to expose config settings for navigation service. This can be achieve using Get.config:
Get.config(
enableLog = true,
defaultPopGesture = true,
defaultTransition = Transitions.cupertino}
Regards,
Stacked seems very interesting, as it answers many of the architecture decisions a developer needs to make. Nice work @FilledStacks
As people start adopting this package, you will need help debugging the issues that arise, and implement new features. You will also need a technical writer, and give Stacked a website of its own.
Early adopters will test any new idea. But if you want to give this package a serious adoption rate, you need to get more devs on your team. I have seen how small teams have stifled projects. I makes sense to find a trusted second man for the project and its Github account.
I'm trying to hold a list of items and use it in multiple viewmodels. Currently, I could achieve this by manually notifying view models.
How would you implement multiple streams with the StreamViewModel
currently? I would imagine combining and transforming streams to get multiple types out of them could get tedious.
I would think the best implementation would be the way you set up Provider previously:
providers = [
..._independentServices,
..._dependentServices,
..._consumableServices,
];
Maybe it would it make sense to have something like this:
abstract class StreamsViewModel extends BaseViewModel {
List<StreamsMixin<T>> get streams;
StreamsViewModel(){
_listenToStreams(streams);
}
void _listenToStreams(List<StreamsMixin> streams) {
_streams = streams;
for (var stream in _streams) {
stream.addListener(_indicateChange);
}
}
Where the StreamsMixin
does the same thing that the StreamViewModel
does. Does that make sense? If I'm missing something and this is already doable, then an example of multiple streams would go a long way. Thanks!
Add the proper documentation for each of the Stacked Services that are provided into the readme. This should contain the reason they exist, the best way to make use of them and go through all the features that they provide.
I would suggest to add a method to the BaseViewModel that checks if any of the values of the busy map are still true. This is useful for specific use cases, like displaying a small loading indicator in app that tells the user that processes are running. To find that out in the current implementation, the viewModel would need to keep track of all the Objects it has used to set the busy state separately, which is unnecessarily verbose when the BaseViewModel could just expose a simple function.
For example:
return _busyStates.values.any((busy) => busy);
The packages should have proper LICENSE
files. These files are included in the assets for any Flutter app that uses one the packages.
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building Builder(dirty, dependencies: [MediaQuery]):
Failed assertion: boolean expression must not be null
The relevant error-causing widget was
MaterialApp
lib\…\global\app_widget.dart:13
When the exception was thrown, this was the stack
#0 setupCustomDialogUI.<anonymous closure>
package:split_it/…/utils/custom_dialog.dart:47
#1 DialogService.showCustomDialog.<anonymous closure>.<anonymous closure>
package:stacked_services/…/dialog/dialog_service.dart:145
#2 Builder.build
package:flutter/…/widgets/basic.dart:6998
#3 StatelessElement.build
package:flutter/…/widgets/framework.dart:4576
#4 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4502
...
════════════════════════════════════════════════════════════════════════════════
The issue is with this piece of code, it says boolean expression must not be null
. Which means the dialogRequest.showIconInMainButton
does not have a default value so I have to add this when showing a dialog:
await dialogService.showCustomDialog(
title: 'My custom dialog',
description: 'This is my dialog description',
mainButtonTitle: 'Confirm',
// this argument is not required nor does it have a default value which makes it weird and it
// makes an error, hope you got my point
showIconInMainButton: true,
);
This is not so obvious nor documented. So I hope you can add a default value for dialogRequest.showIconInMainButton
.
Hi Dane,
So whenever i try to use futurebuilder inside my class where my ViewModelBuilder is reactive i am getting the following error my model class extending ReactiveViewModel
setState() or markNeedsBuild() called during build flutter
Taking a look at the implementation of onModelReady
, I can tell that the following two snippets of code do the exact same thing:
ViewModelBuilder.reactive(
viewModelBuilder: () => FooViewModel(),
onModelReady: (viewModel) => viewModel.init(),
builder: ..,
)
// does the same as the following more concise snippet:
ViewModelBuilder.reactive(
viewModelBuilder: () => FooViewModel()..init(),
builder: ..,
)
So what is the point of onModelReady
, now that you removed the onPostFrameCallback
?
Man, can you read minds?! :-D I just switchted my project-architecture to stacked, which clearly represents the architecture, I was searching for and works very well. And 2 days ago, I was working on a problem, where I thought, "hmmm.. That's something where a MultipleStreamViewModel would come in handy" ... and ... boom, the next day - there it is!
Unfortunatelly, there's no documentation providing how to set it up. Could you provide some in the README?
Cheers!
Image a view model which has a TextEditingController
to get the text from a TextField
in the view. This controller can be initialized with the help of ViewModelBuilder.onModelReady
but how would it be disposed correctly?
While exploring around with the StreamViewModel, I noticed it also has an option to call initialise().
By doing this, it does not get the stream since it overrides your implementation and logic thus initialise() does not do anything in this context.
If there is any other use case for this being here do let me know as I felt it is a bug, not sure I might be wrong but I thought it's worth bringing up :)
PS - Awesome work on the Stacked package, I've been following @FilledStacks since your TikTok UI video and it's been a great learning curve where I've learned and deployed few production apps already with it!
Hello,
Looks like dependency on get is outdated for anything
Because my_app depends on stacked_services any which depends on get ^1.17.3, get ^1.17.3 is required.
Getting error from compiler:
Launching lib\main.dart on Android SDK built for x86 in debug mode...
Compiler message:
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:279:8: Error: The method 'SnackRoute.install' has more required arguments than those of overridden method 'OverlayRoute.install'.
void install(OverlayEntry insertionPoint) {
^
/C:/flutter/packages/flutter/lib/src/widgets/routes.dart:41:8: Context: This is the overridden method ('install').
void install() {
^Compiler message:
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:279:8: Error: The method 'SnackRoute.install' has more required arguments than those of overridden method 'OverlayRoute.install'.
void install(OverlayEntry insertionPoint) {
^
/C:/flutter/packages/flutter/lib/src/widgets/routes.dart:41:8: Context: This is the overridden method ('install').
void install() {
^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/routes/default_route.dart:242:9: Error: No named parameter with the name 'animation'.
animation: animation,
^^^^^^^^^
/C:/flutter/packages/flutter/lib/src/cupertino/route.dart:436:3: Context: Found this candidate, but the arguments don't match.
CupertinoFullscreenDialogTransition({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:289:18: Error: Too many positional arguments: 0 allowed, but 1 found.
Try removing the extra positional arguments.
super.install(insertionPoint);
^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/routes/default_route.dart:242:9: Error: No named parameter with the name 'animation'.
animation: animation,
^^^^^^^^^
/C:/flutter/packages/flutter/lib/src/cupertino/route.dart:436:3: Context: Found this candidate, but the arguments don't match.
CupertinoFullscreenDialogTransition({
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
../../AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/get-1.17.3/lib/src/snackbar/snack_route.dart:289:18: Error: Too many positional arguments: 0 allowed, but 1 found.
Try removing the extra positional arguments.
super.install(insertionPoint);
^
Target kernel_snapshot failed: Exception: Errors during snapshot creation: null
build failed.FAILURE: Build failed with an exception.
Where:
Script 'C:\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 896What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.Process 'command 'C:\flutter\bin\flutter.bat'' finished with non-zero exit value 1
Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.Get more help at https://help.gradle.org
BUILD FAILED in 23s
Exception: Gradle task assembleDebug failed with exit code 1
Exited (sigterm)
This is version:
[√] Flutter (Channel dev, 1.18.0-9.0.pre, on Microsoft Windows [Version 10.0.18362.778], locale en-US)
Any ideas how to get around this?
When using the NavigationService to navigate to a new view, how do you access the navigation arguments in the ViewModel of the new View, I can't see anything in the NavigationService class that gives access to the passed arguments?
Hi,
I was checking the documentation and there's an error on BaseViewModel example:
class WidgetOne extends StatelessWidget {
...
// A bit silly to pass the same property back into the viewmodel
// but here it makes sense
child: model.busy(model.currentHuman) // This return true if we have the model
? Center(
child: CircularProgressIndicator(),
)
: Container(/* Human Details styling */)
),
),
);
}
}
It should be this:
class WidgetOne extends StatelessWidget {
...
// A bit silly to pass the same property back into the viewmodel
// but here it makes sense
child: model.busy(model.currentHuman) // This return true if we have the model
? Container(/* Human Details styling */)
: Center(
child: CircularProgressIndicator(),
)
),
),
);
}
}
This is based on the BaseViewModel implementation:
Map<int, bool> _busyStates = Map<int, bool>();
/// Returns the busy status for an object if it exists. Returns false if not present
bool busy(Object object) => _busyStates[object.hashCode] ?? false;
Regards,
I am not sure if this should be a feature request as I might be misunderstanding the proper usage of the stacked
package in this scenario.
Essentially: what do I do if I need a view model that reacts to the widget tree.
Say, I have the following:
// A widget contains a ViewModelBuilder
viewModelBuilder: () => AppleViewModel(apples)
Now, the AppleViewModel
will be initalized with 10
. However, the AppleWidget
might change and have 11
apples at a later point. Unfortunately, the view model needs to act differently with 11
apples. However, the viewModelBuilder
will not be called again.
How do I update a view model based on a value that changed in the widget tree?
To add a little bit of context, the apples
value would also be controlled by a view model. Thus, I basically want to update a view model from a parent view model.
If this is not yet implemented and I am not missing how to properly use the architecture, this would be a feature request 🙂 (let me know if my train of thought here is incorrect altogether)
Usually, this task would be accomplished using a StatefulWidget
and to my understanding, I should not use them with stacked
. In a StatefulWidget
, I have access to didChangeDependencies
, which allows me to solve the above problem using Provider
/InheritedWidget
or I can use didUpdateWidget
if the value is passed as a parameter.
In a few of the implementations using v1 of this architecture (packaged in provider_architecture) there were many applications that make use of streams from the services. In most cases of using firestore this will be the case. The usual implementation of a ViewModel that makes use of a stream will look like below.
class OrdersViewModel extends BaseViewModel {
final OrderService orderService = locator<OrderService>();
List<Order> _orders;
List<Order> get orders => _orders;
Future initialise() {
setBusy(true);
orderService.ordersStream.listen((incomingOrders) {
_orders = incomingOrders;
notifyListeners();
setBusy(false);
}
}
}
That's a general implementation of consuming a stream of a certain type from a service. To make is clear, I know it's possible to use the StreamBuilder to consume a stream but I want the ViewModel to control everything, I also like having that place where I can log the data.
That's the reason I am now suggesting that we implement a StreamViewModel along with the BaseViewModel and ReactiveViewModel that already exists. It will take care of the above boilerplate code for us along with disposing. I like the way that the StreamBuilder implements their functionality so something similar would be great.
An abstract class that requires you to override required methods and implements some base functionality.
In addition to above we also should be able to insert some additional logic before setting the data. This could be where we log data, transform the incoming date or add additional logic. For instance in one of the apps we built we had to close a details panel if the order that's selected is not in the orders returned. That calculation can be done here before notifyListeners is called.
We have to ensure that the stream is disposed properly and rebuilds of the widget does not cause any problems.
Is this package a combination of responsive_builder and provider architecture packages (best of both worlds)?
Does this package support responsive layouts out of the box (different phone sizes)?
Awesome package. Thanks!!
In many cases, business logic needs to access a value of another ViewModel, such as the selected Id to request data, how to solve ? thanks for answer
How would you initialize/use MultipleStreamViewModel
when a Stream
which needs to take in parameters? And those parameters change over the lifetime of your model; and you have to cancel that stream and listen to a new stream with new values for your parameters.
e.g.:
Stream<int> getIdsByDate(DateTime date) async* {
...
}
I have several StatefulWidgets that use exactly this syntax:
onModelReady: (model) => model.initialise(),
The model.initialise method does a setBusy(true), then gets on with reading data.
However, the first build of the page completes before setBusy(true) is set and notified, causing errors with incomplete data from the model.
I also tried this: onModelReady: (model) async => await model.initialise(),
Is there an easy way to halt until the onModelReady method has completed?
When I want to import package:stacked/stacked.dart
my IDE always recommends package:stacked/_reactive_service_mixin.dart
etc.
I think that only the library file should be publicly visible.
We can currently use provider just by importing stacked.
import 'package:provider/provider.dart';
. VSCode won't auto import it.Generally, it's considered a best practice to organize changelogs latest first.
Most changelogs in pub read most recent first, however stacked reads in reverse.
This means that if you're subscribing to pub changes via an RSS reader, or just viewing the changes on pub.dev, you will always have to scroll to see the latest version.
Hi,
i migrated to this package from your ArchitectureV3 and now facing the following issue;
I have a onModelReady Function with an await Future. Inside this future i call setBusy to true at start, another await Future in the Mid and than setBusy to false at End. When i for e.g. navigate Back before the Future returns, the last setBusy cause the error, that the Model no longer can be used.
The Workaround is to create a mounted variable suggested by Remi, but as we now have many Models, i wonder how to do it without having this boilerplate variable.
Example:
ViewModelBuilder<Model>.reactive(
onModelReady: (model) async {
await model.someFuture(parmeter: parameter);
},
viewModelBuilder: () => Model(),
builder: (context, model, child) {
return [....]
});
class Model extends BaseViewModel {
setbusy(true);
Future.delayed(Duration(seconds: 5)).then((value) => setBusy(false));
}
Iam doing something wrong?
Hi
First of all thank u for this awesome package .
Second
I’m getting this error when I’m using clearStackAndShow method to navigate .
Unhandled Exception: Bad state: Future already completed
method pushNamedAndRemoveUntil works fine though .
Using Version ^0.2.2 .
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.