A Flutter application architecture created from real world scenarios

Related tags

Templates stacked
Overview

Stacked

An architecture developed and revised by the FilledStacks community. This architecture was initially a version of MVVM as described in this video. Since then Filledstacks app development team has built 6 production applications with various requirements. This experience along with countless requests for improvements and common functionality is what sparked the creation of this architecture package. It aims to provide common functionalities to make app development easier as well as code principles to use during development to ensure your code stays maintainable.

If you're reading this disclaimer, the series that does a deep-dive on this architecture has not been published yet.

How it works

The architecture is very simple. It consists of 3 major pieces, everything else is up to your implementation style. These pieces are:

  • View: Shows the UI to the user. Single widgets also qualify as views (for consistency in terminology) a view, in this case, is not a "Page" it's just a UI representation.
  • ViewModel: Manages the state of the View, business logic, and any other logic as required from user interaction. It does this by making use of the services
  • Services: A wrapper of a single functionality/feature set. This is commonly used to wrap things like showing a dialog, wrapping database functionality, integrating an API, etc.

Let's go over some of those principles to follow during development.

  • Views should never MAKE USE of a service directly.
  • Views should only contain logic if necessary. If the logic is from UI only items then we do the least amount of required logic and pass the rest to the ViewModel.
  • Views should ONLY render the state in its ViewModel.
  • ViewModels for widgets that represent page views are bound to a single View only.
  • ViewModels may be re-used if the UI requires the same functionality.
  • ViewModels should not know about other ViewModels.

That's quite a bit of "rules", but they help during production. Trust me.

Stacked's place in your architecture

Stacked provides you with classes and functionalities to make it easy to implement that base architecture that this package is built for. There are additional things that you can add to your application that will make the usage of this architecture much more pleasant. This will be discussed in full on the architecture series that will come out soon. Everything from navigation, dependency injection, service location, error handling, etc.

Packages

In the effort of providing as much value with the stacked package as possible, the repo contains all of the other packages that extend the stacked functionality further and implements some of the base functionalities for you. It also contains third-party extensions that can be used with stacked.

Package Pub
stacked pub package
stacked_generator pub package
stacked_services pub package
stacked_hooks pub package
stacked_themes pub package
stacked_crashlytics pub package
stacked_firebase_auth pub package
stacked_localisation pub package
stacked_localisation_generator pub package

Each package folder contains instructions on how to use the package so please look at the README per package for detailed examples.

Migrating from provider_architecture to Stacked

Let's start with a statement to ease your migration panic 😅 stacked is the same code from provider_architecture with name changes and removal of some old deprecated properties. If you don't believe me, open the repo's side by side and look at the lib folders. Well, up till yesterday (22 April 2020) I guess when I updated the BaseViewModel. I wanted to do this to show that stacked is production-ready from the go. It's a new package but it's been used by all of you and the FilledStacks development team for months in the form of provider_architecture. With that out of the way, let's start the migrate.

ViewModelProvider Migration

This class has now been more appropriately named ViewModelBuilder. This is to match it's functionality more closely. Building UI FROM the ViewModel. The ViewModel is used to drive the state of the reactive UI.

Migrations to take note of:

  • ViewModelProvider -> ViewModelBuilder
  • Named constructor withoutConsumer is now called nonReactive
  • Named constructor withConsumer is now called reactive
  • Instead of passing a constructed ViewModel which was constructing every rebuilder we pass a viewModelBuilder. A function that returns a ChangeNotifier.
  • reuseExisting has changed to disposeViewModel and now has a default value of true. If you used reuseExisting=true it has to change to disposeViewModel=false.

Let's look at that in code. We'll go over withoutConsumer/nonReactive first

class HomeViewMultipleWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<HomeViewModel>.withoutConsumer(
      viewModel: HomeViewModel(),
      onModelReady: (model) => model.initialise(),
      reuseExisting: true,
      builder: (context, model, _) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[TitleSection(), DescriptionSection()],
        ),
      ),
    );
  }
}

Will Change to

class HomeViewMultipleWidgets extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<HomeViewModel>.nonReactive( // Take note here
      viewModelBuilder: () => HomeViewModel(), // Take note here
      disposeViewModel: false, // Take note here
      onModelReady: (model) => model.initialise(),
      builder: (context, model, _) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            model.updateTitle();
          },
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[TitleSection(), DescriptionSection()],
        ),
      ),
    );
  }
}

For the withConsumer function we do the following

class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelProvider<HomeViewModel>.withConsumer(
    );
  }
}

Changes to

class HomeView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ViewModelBuilder<HomeViewModel>.reactive( // Take note here

    );
  }
}

ProviderWidget Migration

The only change here was the name.

class DuplicateNameWidget extends ProviderWidget<Human> {

}

// Becomes

class DuplicateNameWidget extends ViewModelWidget<Human> {

}

The rest of the package is all new functionality which can be seen above. Please check out the issues for tasks we'd like to add. If you would like to see any functionality in here please create an issue and I'll assess and provide feedback.

Comments
  • Error when loading screen

    Error when loading screen

    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.
    
    opened by brianschardt 29
  • [stacked_services] Null check operator used on a null value in showCustomSnackbar

    [stacked_services] Null check operator used on a null value in showCustomSnackbar

    The following exception is thrown when clicking on a custom snackbar without specifying the optional onTap parameter:

    The following _CastError was thrown while handling a gesture:
    Null check operator used on a null value
    
    When the exception was thrown, this was the stack
    #0      SnackbarService.showCustomSnackBar.<anonymous closure>
    package:stacked_services/…/snackbar/snackbar_service.dart:191
    #1      SnackbarController._getBodyWidget.<anonymous closure>.<anonymous closure>
    package:get/…/snackbar/snackbar_controller.dart:236
    #2      GestureRecognizer.invokeCallback
    package:flutter/…/gestures/recognizer.dart:198
    #3      TapGestureRecognizer.handleTapUp
    package:flutter/…/gestures/tap.dart:613
    #4      BaseTapGestureRecognizer._checkUp
    package:flutter/…/gestures/tap.dart:298
    #5      BaseTapGestureRecognizer.acceptGesture
    package:flutter/…/gestures/tap.dart:269
    #6      GestureArenaManager.sweep
    package:flutter/…/gestures/arena.dart:157
    #7      GestureBinding.handleEvent
    package:flutter/…/gestures/binding.dart:449
    #8      GestureBinding.dispatchEvent
    package:flutter/…/gestures/binding.dart:425
    #9      RendererBinding.dispatchEvent
    package:flutter/…/rendering/binding.dart:329
    #10     GestureBinding._handlePointerEventImmediately
    package:flutter/…/gestures/binding.dart:380
    #11     GestureBinding.handlePointerEvent
    package:flutter/…/gestures/binding.dart:344
    #12     GestureBinding._flushPointerEventQueue
    package:flutter/…/gestures/binding.dart:302
    #13     GestureBinding._handlePointerDataPacket
    package:flutter/…/gestures/binding.dart:285
    #17     _invoke1 (dart:ui/hooks.dart:170:10)
    #18     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7)
    #19     _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
    (elided 3 frames from dart:async)
    Handler: "onTap"
    

    Here's line 191 in snackbar_service.dart:

    onTap: (object) => onTap!(),

    And cheers for all your work on Stacked -- it's great

    p2 
    opened by ryan-holder 19
  • [Stacked Themes] The plugin `flutter_statusbarcolor_ns` uses a deprecated version of the Android embedding

    [Stacked Themes] The plugin `flutter_statusbarcolor_ns` uses a deprecated version of the Android embedding

    The plugin flutter_statusbarcolor_ns uses a deprecated version of the Android embedding. To avoid unexpected runtime failures, or future build failures, try to see if this plugin supports the Android V2 embedding. Otherwise, consider removing it since a future release of Flutter will remove these deprecated APIs. If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration. i was going to open an issue to the author but he closed the issue section. fyi this happened in the latest flutter update 2.5

    p1 Improvement 
    opened by ajnaf 19
  • Stacked localisation Package - Implementation Discussion and Requirements

    Stacked localisation Package - Implementation Discussion and Requirements

    Motivation

    A question that comes up often is how to deal with localisation in stacked. I try my best to make stacked something that builds on top of Flutter meaning that everything you can currently do in Flutter should be able to be done the same using stacked. That means localisation can work exactly the same as well. Given that we've implemented localisation 3 times, once with my way through a LocalisationService and twice using the Flutter style setup I have clear examples of how I'd like to implement localisation.

    Description

    This issue will details what I am thinking of implementing in hopes that I can get feedback on certain parts or pointers to work with. The way that I plan an implementation or package is by defining exactly how I'd like to use it in the application. Then I reverse engineer that code to produce concrete details for the implementation

    Dream Localisation Usage

    I would personally like localisation to be available in the models and services for three reasons:

    1. Easily rebuild the UI if the strings have been updated
    2. Allow for easier translation of exceptions
    3. Allow translation of strings with String reliant services like the dialog service

    These are a few examples of what I'd like to do in code.

    1. Translation in the View (UI) files

     Widget build(BuildContext context) {
        return ViewModelBuilder<HomeViewModel>.reactive(
          builder: (context, model, child) => Scaffold(
            body: Text(model.translate(HomeViewStrings.title))
          ),
          viewModelBuilder: () => HomeViewModel(),
        );
      }
    

    2. Use translated exceptions (if needed)

    throw Exception(translate(HomeViewScreen.feedFetchFailedException));
    

    3. Show dialog

    _dialogService.showDialot(title: translate(HomeViewScreens.dialogSuccessTitle));
    

    That is how i'd like to use it in code. This is obviously personal opinion but I think if I look at it objectively that it's the cleanest and least intrusive approach to localisation. So lets reverse engineer this syntax and figure out what's required to build this localisation service.

    Implementation

    The first thing we'll need it the functionality to get strings from the ViewModel. This will be a simple function that indexes into the LocalisationService and requests the string for a key.

    class LocalisedViewModel {
    
    String translate(String key) => _localisationService[key];
    
    }
    

    This means in the localisation service we would need the following

    class LocalisationService {
      // this value will be reloaded when the app is started up or when it comes back from the background.
      Map<String, String> _localisedStrings;
    
      /// Returns the value from the localisedStrings dictionary or the key that was requested.
      String operator[](String key) => _localisedStrings[key] ?? key;
    
      Future initialise() {
        // get the current locale
        // load the strings from disk into the _localisedStrings
      }
    }
    

    This is an example of how a language file would look that would be used with the keys above

    {
      "HomeViewStrings" : {
         "title" : "Home View Title",
         "dialogSuccessTitle" : "You have successfully done something on HomeView",
         "feedFetchFailedException" : "We could not fetch your home feed. Please check connection and try again",
      }
    }
    

    The type safe String keys generated will look like this.

    // This title comes from the object in the json file above
    class HomeViewStrings {
      static String title = 'title';
      static String dialogSuccessTitle =  'dialogSuccessTitle';
      static String feedFetchFailedException = 'ffeedFetchFailedException';
    }
    

    Dynamic Values

    There are other parts of the service that need to be implemented as well like dynamic value substitution which can be done by passing in a set of replacements into the translate function. If you take an example string like this

    {
      "CounterViewStrings" : {
         "timesCounted" : "You have tapped {0} times",
      }
    }
    

    we could change the translate function to this.

    String translate(String key, {List<dynamic> replacements} {
      var stringFromFile = _localisationService[key];
      for(int i = 0; i < replacements.length; i++) {
        stringFromFile = stringFromFile.replace('{$i}, replacements[i]);
      }
    
      return stringFromFile;
    }
    

    This would allow us to handle dynamic values by using a positioned based replacement function.

    Conclusion

    That's basically it. The things that would have to be done for this package to be complete is:

    • [x] Create the localisation service
    • [x] Create a generator to generate the String key files
    • [x] Write the Readme instructions
    • [x] publish the package
    p2 
    opened by FilledStacks 18
  • [stacked] Updating View Models from a parent

    [stacked] Updating View Models from a parent

    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)

    StatefulWidget

    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.

    opened by creativecreatorormaybenot 17
  • [Stacked Services] currentRoute is always empty

    [Stacked Services] currentRoute is always empty

    Hello there,

    I am using the navigation service in my app..

    I have a view (QR scanner view) that is accessable from 2 places..

    the first place is as a startup view,,, in this case I am showing a button to take me to the dashboard..

    the second place is in the dashboard... in this case I am not showing a button to take me to the dashboard, I am just using the back button.

    when I access the view from the dashboard simply by calling locator<NavigationService>().navigateTo(QRScannerRoute);

    In the QRScannerView I want to check if my currentRoute is empty "which means It is the startup view" Show the link to take me to the dashboard... but if my current route is not empty which indicates I accessed it from the dashboard just use the default backButton... But in all the cases currentRoute is always empty,,, same applies to the previous route..

    To summarise...

    locator<NavigationService>().navigateTo(QRScannerRoute);

    leads to empty currentRoute in the target view always.

    also I couldn't find a way to access the navigation stack to check if it is empty or not..

    new feature 
    opened by bdairy 16
  • Issue running app on release mode in flutter web

    Issue running app on release mode in flutter web

    Since upgrading to the latest version of stacked and using only stacked for routing and dependency injection I can't run the app on release mode in flutter web. I've added the error logs obtained from the web console. App works perfectly in debug mode.

    main.dart.js:40167 Uncaught TypeError: Cannot read property 'cy' of null at aNJ.a6L (main.dart.js:87425) at aNJ.$1$3$instanceName$param1$param2 (main.dart.js:87427) at aNJ.$3$instanceName$param1$param2 (main.dart.js:87430) at aAv.$1$0 (main.dart.js:112800) at Object.buV (main.dart.js:15228) at main.dart.js:23709 at aZ6.a (main.dart.js:3939) at aZ6.$2 (main.dart.js:39137) at Object.n (main.dart.js:3925) at Object.b_m (main.dart.js:23717)

    documentation 
    opened by jdaev 15
  • notifySourceChanged not changing streams in MultipleStreamViewModel

    notifySourceChanged not changing streams in MultipleStreamViewModel

    Hi,

    As the title implies, Streams are not changing when calling notifySourceChanged. Only after killing the app the streams change.

    Regards,

    Edit:

    I'm using firestore to get 2 streams. The issue here is that if I logout and login with a different user, the stream is giving me the previous user. If I add a value, the stream gets the correct stream (new user). I'm using the exact example from the repo but with firestore.

    reproduce issues Improvement 
    opened by ghost 15
  • Modifying Dialog Service

    Modifying Dialog Service

    This PR has the following modifications:

    • Modified the dialog service so that it can handle platform choosing for dialog designs 👍, more info in the file where it is documented 📜✨ • Modified example app to show how the dialog service works now. 🚀

    opened by YazeedAlKhalaf 15
  • [stacked_generator] type '_CompactLinkedHashSet<String?>' is not a subtype of type 'Set<String>' in type cast

    [stacked_generator] type '_CompactLinkedHashSet' is not a subtype of type 'Set' in type cast

    I'm getting an error when launching

    flutter pub run build_runner watch --delete-conflicting-outputs
    

    Source code

    pubspec.yaml

    dependencies:
      ...
      stacked: ^2.1.3
    
      stacked_services: ^0.8.6
    
    dev_dependencies:
      ...
      build_runner:
    
      stacked_generator: ^0.4.2
    

    app.dart

    @StackedApp(
      routes: const [
        const MaterialRoute(page: HomeView, initial: true),
      ],
    )
    class App {}
    

    Logs

    [INFO] Generating build script...
    [INFO] Generating build script completed, took 772ms
    
    [INFO] Setting up file watchers...
    [INFO] Setting up file watchers completed, took 28ms
    
    [INFO] Waiting for all file watchers to be ready...
    [INFO] Waiting for all file watchers to be ready completed, took 198ms
    
    [INFO] Initializing inputs
    [INFO] Reading cached asset graph...
    [INFO] Reading cached asset graph completed, took 111ms
    
    [INFO] Checking for updates since last build...
    [INFO] Checking for updates since last build completed, took 1.4s
    
    [INFO] Running build...
    [INFO] 1.2s elapsed, 1/2 actions completed.
    [INFO] 2.4s elapsed, 1/2 actions completed.
    [INFO] 3.5s elapsed, 1/2 actions completed.
    [INFO] 4.5s elapsed, 1/2 actions completed.
    [INFO] 20.3s elapsed, 1/2 actions completed.
    [WARNING] No actions completed for 19.8s, waiting on:
      - stacked_generator:stackedFormGenerator on lib/app/app.dart
    
    [SEVERE] stacked_generator:stackedRouterGenerator on lib/app/app.dart:
    
    type '_CompactLinkedHashSet<String?>' is not a subtype of type 'Set<String>' in type cast
    [INFO] Running build completed, took 21.1s
    
    [INFO] Caching finalized dependency graph...
    [INFO] Caching finalized dependency graph completed, took 89ms
    
    [SEVERE] Failed after 21.2s
    
    
    opened by kodjosama 14
  • Add Text-To-Speech functionality by creating TtsService

    Add Text-To-Speech functionality by creating TtsService

    This PR has the following modifications:

    • Added a new service to Stacked Services called TtsService, aka Text-To-Speech Service. • Modified example app to show how the TtsService works by creating a new screen. • Upgraded minSdkVersion to 21.

    All those things are under one commit since they are related and would not make sense to make each one as a separate commit.

    opened by YazeedAlKhalaf 14
  • Stacked Cli - update create app template - dialogs/bottomsheets to make use of @StackedApp

    Stacked Cli - update create app template - dialogs/bottomsheets to make use of @StackedApp

    When creating a flutter project with stacked cli - the dialogs & bottomsheets are not configured via @StackedApp - instead they are present inside ui/setup

    @StackedApp( dialogs: [ // @stacked-dialog StackedDialog(classType: InfoAlertDialog), ], bottomsheets: [ // @stacked-bottomsheet StackedBottomsheet(classType: NoticeSheet), ],

    Improvement new feature 
    opened by rsenthilkumar6 0
  • Flutter Web redirect support

    Flutter Web redirect support

    Currently Navigation works fine also on web, however there is no support to protect named routes in Flutter Web. Go_router provides a redirection function on each new navigation event or at the route level for a specific route. We use Stacked in a corporate environment and would love to use it in our Web projects too. Currently as a workaround we are using go_router in our Web Projects. Since Flutter 3 with better support for Web, Flutter Web gains more traction therefore it would be super nice if Stacked provides support for this and general Web support. I would offer myself to implement the feature when being guided from the stacked team. I need to understand how it works at the moment and would love to discuss how this can be implemented, maybe in a call to get deeper insights from stacked navigation. Cheers!

    Improvement new feature 
    opened by mraaz97 3
  • [stacked_services] showSnackbar does not use same SnackbarConfig fields as showCustomSnackBar

    [stacked_services] showSnackbar does not use same SnackbarConfig fields as showCustomSnackBar

    When using a default snack bar via the showSnackbar method the following SnackbarConfig values are not used:

    • icon
    • maxWidth
    • padding
    • borderRadius
    • borderColor
    • borderWidth
    • leftBarIndicatorColor
    • boxShadows
    • backgroundGradient
    • dismissDirection
    • showProgressIndicator
    • progressIndicatorController
    • progressIndicatorBackgroundColor
    • progressIndicatorValueColor
    • snackStyle
    • forwardAnimationCurve
    • reverseAnimationCurve
    • animationDuration
    • overlayBlur
    • overlayColor
    • userInputForm

    It would be nice to be able to set these values using SnackbarConfig and have them apply to the basic showSnackbar without having to create a custom snack bar.

    opened by TrentStarky 1
  • A few nice things to have

    A few nice things to have

    I am just starting to use stacked and I am really enjoying it. tks for doing that! Previously I was using code I got from https://github.com/Kavantix/hn_state_example.git, which has a similar approach, and I think there are a few good things there that seems easy to implement here. I say 'seems' because I am kinda new to programming and so I might be missing some stuff. For example, the view model builder has some optional params like loaderBuilder, errorBuilder, showLoader, showError that simply reflects on the build method, but it makes it easier to deal with those situations:

      @override
      Widget build(BuildContext context) {
        _model.refreshProvided(context);
        return (widget.showLoader ?? false || widget.showError ?? false)
            ? AnimatedSwitcher(
                duration: const Duration(milliseconds: 330),
                child: _model.isLoading && (widget.showLoader ?? false)
                    ? widget.loaderBuilder?.call(context) ??
                        const Center(
                          child: CircularProgressIndicator(),
                        )
                    : _model.hasError && (widget.showError ?? false)
                        ? widget.errorBuilder?.call(context, _model) ??
                            ConnectionErrorIndicator(model: _model)
                        : widget.builder(context, _model, widget.child),
              )
            : widget.builder(context, _model, widget.child);
      }
    
    class ConnectionErrorIndicator extends StatelessWidget {
      final BaseModel model;
      final double topPadding;
    
      const ConnectionErrorIndicator({
        Key key,
        @required this.model,
        this.topPadding = 24,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              SizedBox(height: topPadding),
              const Icon(Icons.error),
              const SizedBox(height: 16),
              Text(
                Strings.of(context).errors.failedLoading,
                textAlign: TextAlign.center,
              ),
              ElevatedButton(
                onPressed: model.retryLoading,
                child: Text(Strings.of(context).errors.tryAgain),
              ),
            ],
          ),
        );
      }
    }
    

    Also, as you can see from about, the base model that extends changenotifer has a retryLoading method:

    abstract class BaseModel extends ChangeNotifier implements Providable {
      bool _isLoading = false;
      bool get isLoading => _isLoading;
    
      bool _hasError = false;
      bool get hasError => _hasError;
    
      VoidCallback _retryCallback;
    
      var _isDisposed = false;
    
      bool startLoading() {
        if (isLoading || hasError) return false;
        _isLoading = true;
        notifyListeners();
        return true;
      }
    
      void doneLoading() {
        assert(isLoading);
        _isLoading = false;
        notifyListeners();
      }
    
      void loadingFailed(VoidCallback retryCallback) {
        assert(isLoading);
        _retryCallback = retryCallback;
        _isLoading = false;
        _hasError = false;
        notifyListeners();
      }
    
      void retryLoading() {
        if (!hasError || _retryCallback == null) return;
        _hasError = false;
        _retryCallback();
        _retryCallback = null;
        notifyListeners();
      }
    
      @override
      @mustCallSuper
      void notifyListeners() {
        if (_isDisposed) return;
        super.notifyListeners();
      }
    
      @override
      @mustCallSuper
      void dispose() {
        _isDisposed = true;
        super.dispose();
      }
    
      @override
      void refreshProvided(BuildContext context) {}
    
      bool get usesAppLifecycle => false;
    
      void didChangeAppLifecycleState(AppLifecycleState state) {}
    }
    
    Improvement new feature 
    opened by jalvespinto 2
  • Stacked Cli generate bottom sheet

    Stacked Cli generate bottom sheet

    Similar to the views we should be able to scaffold bottom sheets with viewmodels and without. This command should:

    • Create the new enum value (if there isn't one then create the enum)
    • Register the builder to the setupBottomSheetsUi function
    • Create the sheet view file with completer and request passed in
    • Create the viewmodel for the sheet (If the option is true, by default it should be true)
    • Create the viewmodel tests for the sheet if above is true
    new feature 
    opened by FilledStacks 0
Owner
Dane Mackier
Full-Stack applications developer focussed on complete Mobile products.
Dane Mackier
Flutter-world-time-practice - World Time Tutorial App For Flutter

world_time Result of Flutter Tutorial for Beginners Navigation: pushNamed, pop r

Seonghyeon Cho 1 Feb 7, 2022
A set of real world timelines to showcase the use of timeline_tile package, built with Flutter.

beatiful_timelines Beautiful timelines built with Flutter and timeline_tile. Current examples: Timeline Showcase Football Timeline Activity Timeline S

null 233 Dec 21, 2022
App-flutter-real-estate - Real Estate App Built With Flutter

Real Estate App - Flutter Preview video: https://youtu.be/11u0KeymAAs My Twitter

Sangvaleap Vanny 136 Dec 7, 2022
mypro immobilier app created to manage our real estate agency, we can store products, contacts and transactions..

mypro_immobilier A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started

soufiane bouchtaoui 1 Dec 11, 2021
Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to dev

Katsuyuki Mori 2 Apr 9, 2022
Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to developing Flutter apps.

Flutter Architecture Blueprints Flutter Architecture Blueprints is a project that introduces MVVM architecture and project structure approaches to dev

Daichi Furiya 1.5k Dec 31, 2022
Ouday 25 Dec 15, 2022
Flutter-clean-architecture - A simple flutter project developed with TDD and using Clean Architecture principles.

Clean Architecture This is a study project to practice TDD and a good approach of Clean Architecture for flutter projects. It is based on Reso Coder s

Luiz Paulo Franz 8 Jul 21, 2022
Flutter Architecture inspired by Domain Driven Design, Onion and Clean Architecture

Inspiring Domain Driven Design Flutter Architecture Please take a look at my slides to learn more Strategic Domain Driven Design For Improving Flutter

Majid Hajian 324 Dec 25, 2022
Proyect with Clean Architecture / Hexagonal Architecture - Patron BLoC - The MovieDB API

flutter_movies_db A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

null 2 Sep 22, 2022
An application to track bitcoin prices around the world.

![App Brewery Banner](https://github.com/londonappbrewery/Images/blob/master/AppBreweryBanner.png) # Bitcoin Ticker ?? ## Our Goal The object

Aryaman Prakash 1 Jan 7, 2022
World-time - A simple application that tells time from different locations by using worldtime api

world_time A new Flutter project. Getting Started This project is a starting poi

Ryan Egbejule-jalla 1 Feb 6, 2022
A simple flutter application from an Udemy course to exchange the following currencies: real, dollar and euro using HG Brasil API.

cambiador Conversor de moedas como Dollar, Euro e Real. Getting Started This project is a starting point for a Flutter application. A few resources to

Edilson Matola 1 Mar 17, 2022
Tasawq App — Flutter framework and Firebase An application that objectives to display all nearby stores of all kinds and real estate.

Tasawq App — Flutter framework and Firebase An application that objectives to display all nearby stores of all kinds and real estate. Multi-vendor, standard user login to view nearby products and stores for rating, follow-up, messaging and more

null 1 Nov 10, 2022
Sample flutter project to show exchange Rates of leading cryptos to different currencies in the world.

bitcointicker A bitcoin ticker project Getting Started This project is a starting point for a Flutter application. A few resources to get you started

null 0 Feb 15, 2022
A simple "Hello, World" style Flutter app.

Flutter Sample App - Hello World This is a simple Flutter (Dart) app with a single view. The intention of this application is to demonstrate the usage

Bitrise Examples 1 May 9, 2022
A showcase app for the Flutter SDK. Wonderous will educate and entertain as you uncover information about some of the most famous structures in the world.

Wonderous Navigate the intersection of history, art, and culture. Wonderous will educate and entertain as you uncover information about some of the mo

gskinner team 2.3k Dec 29, 2022
Fifa world cup album manager - 8ª edition of Dart Week from Academia do Flutter

DartWeek 8ª edição - FIFA WORLD CUP album manager App para você gerir suas figurinhas da copa! Com o padrão de projeto MVP, usando SetState, Integraçã

Erilândio Santos Medeiros 4 Oct 17, 2022
Breathe is a mental health blogging app where users can join communities of doctors and other users from around the world and both share their problems as well as lend a ear to and help others

?????????????? ?????????????? In a condensed, suffocating society you can feel closed off, when you can't process your emotions and are going through

Soham Sen 3 May 16, 2022