Home
Softono
b

brianegan

Professional software vendor delivering innovative solutions on the Softono platform. Specialized in both open-source and proprietary software development.

Total Products
3

Software by brianegan

flutter_architecture_samples
Open Source

flutter_architecture_samples

# flutter_architecture_samples [![Build Status](https://github.com/brianegan/flutter_architecture_samples/actions/workflows/analyze_test_build.yml/badge.svg?branch=main)](https://github.com/brianegan/flutter_architecture_samples/actions/workflows/analyze_test_build.ymll) [![codecov](https://codecov.io/gh/brianegan/flutter_architecture_samples/branch/main/graph/badge.svg)](https://codecov.io/gh/brianegan/flutter_architecture_samples) <img align="right" src="assets/todo-list.png" alt="List of Todos Screen"> [TodoMVC](http://todomvc.com) for Flutter! Flutter provides a lot of flexibility in deciding how to organize and architect your apps. While this freedom is very valuable, it can also lead to apps with large classes, inconsistent naming schemes, as well as mismatching or missing architectures. These types of issues can make testing, maintaining and extending your apps difficult. The Flutter Architecture Samples project demonstrates strategies to help solve or avoid these common problems. This project implements the same app using different architectural concepts and tools. You can use the samples in this project as a learning reference, as a roughly apples-to-apples comparison of different approaches, or as a starting point for creating your own apps. The focus of this project is on demonstrating how to structure your code, design your architecture, and the eventual impact of adopting these patterns on testing and maintaining your app. You can use the techniques demonstrated here in many different ways to build apps. Your own particular priorities will impact how you implement the concepts in these projects, so you should not consider these samples to be canonical examples. To ensure the focus is kept on the aims described above, the app uses a simple UI. ### Current Samples - [Vanilla Lifting State Up Example](vanilla) ([Web Demo](https://fas-vanilla.netlify.app)) - Uses the tools Flutter provides out of the box to manage app state. - [InheritedWidget Example](inherited_widget) ([Web Demo](https://fas-inherited-widget.netlify.app)) - Uses an InheritedWidget to pass app state down the widget hierarchy. - [Change Notifier + Provider Example](change_notifier_provider) ([Web Demo](https://fas-change-notifier-provider.netlify.app)) - Uses the [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) class from Flutter with [provider](https://pub.dev/packages/provider) package now recommended by the Flutter team. - [Freezed + Provider + Value Notifier](freezed_provider_value_notifier) ([Web Demo](https://fas-freezed-provider-value-notifier.netlify.app)) - Uses the [ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) class from Flutter with [provider](https://pub.dev/packages/provider) package. - [BLoC Example](bloc_flutter) ([Web Demo](https://fas-bloc-flutter.netlify.app/)) - An implementation of the original [BLoC pattern](https://www.youtube.com/watch?v=PLHln7wHgPE&list=PLOU2XLYxmsIIJr3vjxggY7yGcGO7i9BK5&index=13) described by Paolo Soares at DartConf 2018, which uses Sinks for Inputs and Streams for Outputs - [Bloc Library Example](bloc_library) ([Web Demo](https://fas-bloc-library.netlify.app)) - Uses the [bloc](https://pub.dartlang.org/packages/bloc) and [flutter_bloc](https://pub.dartlang.org/packages/flutter_bloc) libraries to manage app state and update Widgets. - [MobX Example](mobx) ([Web Demo](https://fas-mobx.netlify.app)) - Uses the [MobX](https://pub.dev/packages/mobx) library to manage app state and update widgets using `Observables`, `Actions` and `Reactions`. - [Redux Example](redux) ([Web Demo](https://fas-redux.netlify.app)) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets - ["Simple" BLoC Example](simple_bloc_flutter) ([Web Demo](https://fas-simple-bloc.netlify.app/)) - Similar to the BLoC pattern, but uses Functions for Inputs and Streams for Outputs. Results in far less code compared to original BLoC pattern if code sharing with AngularDart apps isn't an important use case for your app. - [Signals Example](signals) ([Web Demo](https://fas-signals.netlify.app)) - Uses the [Signals](https://pub.dev/packages/signals) package by [Rody Davis](https://pub.dev/publishers/rodydavis.com/packages). - [MVI Example](mvi_flutter) ([Web Demo](https://fas-mvi.netlify.app)) - Uses the concepts from [Cycle.JS](https://cycle.js.org/) and applies them to Flutter. - [scoped_model Example](scoped_model) ([Web Demo](https://fas-scoped-model.netlify.app)) - Uses the [scoped_model](https://pub.dartlang.org/packages/scoped_model) library to hold app state and notify Widgets of Updates ### Supporting Code - [integration_tests](integration_tests) - Demonstrates how to write selenium-style integration (aka end to end) tests using the Page Object Model. This test suite is run against all samples. - [todos_repository_core](todos_repository_core) - Defines the core abstract classes for loading and saving data so that storage can be implemented in various ways, such as file storage or firebase for mobile projects, or window.localStorage for web projects. - [todos_repository_local_storage](todos_repository_local_storage) - Implements the todos repository using the file system, window.localStorage, and SharedPreferences as the data source. ### Running the samples ``` cd <sample_directory> flutter run ``` ### Why a todo app? The app in this project aims to be as simple as possible while still showcasing different design decisions and testing scenarios. For more information, see the [app's specification](app_spec.md). ### Be excellent to each other This Repo is meant as a discussion platform for various architectures. Let us debate these ideas vigorously, but let us be excellent to each other in the process! While healthy debate and contributions are very welcome, trolls are not. Read the [code of conduct](code-of-conduct.md) for detailed information. ### Contributing Feel free to join in the discussion, file issues, and we'd love to have more samples added! Please read the [CONTRIBUTING](CONTRIBUTING.md) file for guidance :) ### License All code in this repo is MIT licensed. ## Attribution All of these ideas and even some of the language are directly influenced by two projects: - [TodoMVC](http://todomvc.com) - A Todo App implemented in various JS frameworks - [Android Architecture Blueprints](https://github.com/googlesamples/android-architecture) - A similar concept, but for Android! The UI and app spec was highly inspired by their example. ## Contributors - [Brian Egan](https://github.com/brianegan) - [Maurice McCabe](https://github.com/mmcc007) - [David Marne](https://github.com/davidmarne) - [Pascal Welsch](https://github.com/passsy) - [Larry King](https://github.com/kinggolf) - [Frank Harper](https://github.com/franklinharper) - [Pavel Shilyagov](https://github.com/p69) - [Leo Cavalcante](https://github.com/leocavalcante) - [Greg Perry](https://github.com/AndriousSolutions) - [Felix Angelov](https://github.com/felangel) - [Francesco Mineo](https://github.com/frideosapps) - [Pavan Podila](https://github.com/pavanpodila) - [Kushagra Saxena](https://github.com/kush3107) - [Shakib Hossain](https://github.com/shakib609) - [Mellati Fatah](https://github.com/GIfatahTH) I'd like to thank all of the folks who have helped write new samples, improve the current implementations, and added documentation! You're amazing! :)

Developer Tools Mobile Development
8.9K Github Stars
flutter_redux
Open Source

flutter_redux

# flutter_redux [![Build Status](https://travis-ci.org/brianegan/flutter_redux.svg?branch=master)](https://travis-ci.org/brianegan/flutter_redux) [![codecov](https://codecov.io/gh/brianegan/flutter_redux/branch/master/graph/badge.svg)](https://codecov.io/gh/brianegan/flutter_redux) A set of utilities that allow you to easily consume a [Redux](https://pub.dartlang.org/packages/redux) Store to build Flutter Widgets. This package supports null-safety and is built to work with [Redux.dart](https://pub.dartlang.org/packages/redux) 5.0.0+ and Flutter 3+. ## Redux Widgets * `StoreProvider` - The base Widget. It will pass the given Redux Store to all descendants that request it. * `StoreBuilder` - A descendant Widget that gets the Store from a `StoreProvider` and passes it to a Widget `builder` function. * `StoreConnector` - A descendant Widget that gets the Store from the nearest `StoreProvider` ancestor, converts the `Store` into a `ViewModel` with the given `converter` function, and passes the `ViewModel` to a `builder` function. Any time the Store emits a change event, the Widget will automatically be rebuilt. No need to manage subscriptions! ## Examples * [Simple example](https://github.com/brianegan/flutter_redux/tree/master/example/counter) - a port of the standard "Counter Button" example from Flutter * [Github Search](https://github.com/brianegan/flutter_redux/tree/master/example/github_search) - an example of how to search as a user types, demonstrating both the Middleware and Epic approaches. * [Todo app](https://github.com/brianegan/flutter_architecture_samples/tree/master/redux) - a more complete example, with persistence, routing, and nested state. * [Timy Messenger](https://github.com/janoodleFTW/timy-messenger) - large open source app that uses flutter_redux together with Firebase Firestore. ### Companion Libraries * [flipperkit_redux_middleware](https://pub.dartlang.org/packages/flipperkit_redux_middleware) - Redux Inspector (use [Flutter Debugger](https://github.com/blankapp/flutter-debugger)) for Flutter Redux apps * [flutter_redux_dev_tools](https://pub.dartlang.org/packages/flutter_redux_dev_tools) - Time Travel Dev Tools for Flutter Redux apps * [redux_persist](https://github.com/Cretezy/redux_persist) - Persist Redux State * [flutter_redux_navigation](https://github.com/flutterings/flutter_redux_navigation) - Use redux events for navigation * [flutter_redux_gen](https://marketplace.visualstudio.com/items?itemName=BalaDhruv.flutter-redux-gen) - VS Code Extension to generate redux code. ## Usage Let's demo the basic usage with the all-time favorite: A counter example! Note: This example requires flutter_redux 0.4.0+ and Dart 2! If you're using Dart 1, [see the old example](https://github.com/brianegan/flutter_redux/blob/eb4289795a5a70517686ccd1d161abdb8cc08af5/example/lib/main.dart). ```dart import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; // One simple action: Increment enum Actions { Increment } // The reducer, which takes the previous count and increments it in response // to an Increment action. int counterReducer(int state, dynamic action) { return action == Actions.Increment ? state + 1 : state; } void main() { // Create your store as a final variable in the main function or inside a // State object. This works better with Hot Reload than creating it directly // in the `build` function. final store = Store<int>(counterReducer, initialState: 0); runApp(FlutterReduxApp( title: 'Flutter Redux Demo', store: store, )); } class FlutterReduxApp extends StatelessWidget { final Store<int> store; final String title; FlutterReduxApp({Key key, this.store, this.title}) : super(key: key); @override Widget build(BuildContext context) { // The StoreProvider should wrap your MaterialApp or WidgetsApp. This will // ensure all routes have access to the store. return StoreProvider<int>( // Pass the store to the StoreProvider. Any ancestor `StoreConnector` // Widgets will find and use this value as the `Store`. store: store, child: MaterialApp( theme: ThemeData.dark(), title: title, home: Scaffold( appBar: AppBar(title: Text(title)), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Connect the Store to a Text Widget that renders the current // count. // // We'll wrap the Text Widget in a `StoreConnector` Widget. The // `StoreConnector` will find the `Store` from the nearest // `StoreProvider` ancestor, convert it into a String of the // latest count, and pass that String to the `builder` function // as the `count`. // // Every time the button is tapped, an action is dispatched and // run through the reducer. After the reducer updates the state, // the Widget will be automatically rebuilt with the latest // count. No need to manually manage subscriptions or Streams! StoreConnector<int, String>( converter: (store) => store.state.toString(), builder: (context, count) { return Text( 'The button has been pushed this many times: $count', style: Theme.of(context).textTheme.display1, ); }, ) ], ), ), // Connect the Store to a FloatingActionButton. In this case, we'll // use the Store to build a callback that will dispatch an Increment // Action. // // Then, we'll pass this callback to the button's `onPressed` handler. floatingActionButton: StoreConnector<int, VoidCallback>( converter: (store) { // Return a `VoidCallback`, which is a fancy name for a function // with no parameters and no return value. // It only dispatches an Increment action. return () => store.dispatch(Actions.Increment); }, builder: (context, callback) { return FloatingActionButton( // Attach the `callback` to the `onPressed` attribute onPressed: callback, tooltip: 'Increment', child: Icon(Icons.add), ); }, ), ), ), ); } } ``` ## Purpose One question that [reasonable people might ask](https://www.reddit.com/r/FlutterDev/comments/6vscdy/a_set_of_utilities_that_allow_you_to_easily/dm3ll7d/): Why do you need all of this if `StatefulWidget` exists? My advice is the same as the original Redux.JS author: If you've got a simple app, use the most straightforward thing possible. In Flutter, `StatefulWidget` is perfect for a simple counter app. However, say you have the more complex app, such as an E-commerce app with a Shopping Cart. The Shopping Cart should appear on multiple screens in your app and should be updated by many different types of Widgets on those different screens (An "Add Item to Cart" Widget on all your Product Screens, "Remove Item from Cart" Widget on the Shopping Cart Screen, "Change quantity" Widgets, etc.). Additionally, you want to test this logic, as it's the core business logic to your app! Now, in this case, you could create a Testable `ShoppingCart` class as a Singleton or Create a Root `StatefulWidget` that passes the `ShoppingCart `*Down Down Down* through your widget hierarchy to the "add to cart" or "remove from cart" Widgets. Singletons can be problematic for testing, and Flutter doesn't have a great Dependency Injection library (such as Dagger2) just yet, so I'd prefer to avoid those. Yet passing the ShoppingCart all over the place can get messy. It also means it's way harder to move that "Add to Item" button to a new location b/c you'd need up update the Widgets throughout your app that passes the state down. Furthermore, you'd need a way to Observe when the `ShoppingCart` Changes so you could rebuild your Widgets when it does (from an "Add" button to an "Added" button, as an example). One way to handle it would be to simply `setState` every time the `ShoppingCart` changes in your Root Widget, but then your whole app below the RootWidget would be required to rebuild as well! Flutter is fast, but we should be thoughtful about what we ask Flutter to rebuild! Therefore, `redux` & `redux_flutter` was born for more complex stories like this one. It gives you a set of tools that allow your Widgets to `dispatch` actions in a naive way, then writes the business logic in another place that will take those actions and update the `ShoppingCart` in a safe, testable way. Even more, once the `ShoppingCart` has been updated in the `Store`, the `Store` will emit an `onChange` event. This lets you listen to `Store` updates and rebuild your UI in the right places when it changes! Now, you can separate your business logic from your UI logic in a testable, observable way, without having to Wire up a bunch of stuff yourself! Similar patterns in Android are the MVP Pattern or using Rx Observables to manage a View's State. `flutter_redux` handles passing your `Store` down to all of your descendant `StoreConnector` Widgets. If your State emits a change event, only the `StoreConnector` Widgets and their descendants will be automatically rebuilt with the latest State of the `Store`! This allows you to focus on what your app should look like and how it should work without thinking about all the glue code to hook everything together! ### Contributors * [Brian Egan](https://github.com/brianegan) * [Chris Bird](https://github.com/chrisabird)

JavaScript Libraries & Components Mobile Development
1.6K Github Stars
scoped_model
Open Source

scoped_model

# scoped_model [![Build Status](https://travis-ci.org/brianegan/scoped_model.svg?branch=master)](https://travis-ci.org/brianegan/scoped_model) [![codecov](https://codecov.io/gh/brianegan/scoped_model/branch/master/graph/badge.svg)](https://codecov.io/gh/brianegan/scoped_model) A set of utilities that allow you to easily pass a data Model from a parent Widget down to its descendants. In addition, it also rebuilds all of the children that use the model when the model is updated. This library was originally extracted from the Fuchsia codebase. This Library provides three main classes: * The `Model` class. You will extend this class to create your own Models, such as `SearchModel` or `UserModel`. You can listen to Models for changes! * The `ScopedModel` Widget. If you need to pass a `Model` deep down your Widget hierarchy, you can wrap your `Model` in a `ScopedModel` Widget. This will make the Model available to all descendant Widgets. * The `ScopedModelDescendant` Widget. Use this Widget to find the appropriate `ScopedModel` in the Widget tree. It will automatically rebuild whenever the Model notifies that change has taken place. This library is built upon several features of Flutter: * The `Model` class implements the `Listenable` interface * `AnimationController` and `TextEditingController` are also `Listenables` * The `Model` is passed down the Widget tree using an `InheritedWidget`. When an `InheritedWidget` is rebuilt, it will surgically rebuild all of the Widgets that depend on its data. No need to manage subscriptions! * It uses the `AnimatedBuilder` Widget under the hood to listen to the Model and rebuild the `InheritedWidget` when the model changes. ## Examples * [Counter App](https://github.com/brianegan/scoped_model/tree/master/example) - Introduction to the tools provided by Scoped Model. * [Todo App](https://github.com/brianegan/flutter_architecture_samples/tree/master/scoped_model) - Shows how to write a Todo app with persistence and tests. ## Usage Let's demo the basic usage with the all-time favorite: A counter example! ```dart // Start by creating a class that holds some view the app's state. In // our example, we'll have a simple counter that starts at 0 can be // incremented. // // Note: It must extend from Model. class CounterModel extends Model { int _counter = 0; int get counter => _counter; void increment() { // First, increment the counter _counter++; // Then notify all the listeners. notifyListeners(); } } // Create our App, which will provide the `CounterModel` to // all children that require it! class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) { // First, create a `ScopedModel` widget. This will provide // the `model` to the children that request it. return new ScopedModel<CounterModel>( model: new CounterModel(), child: new Column(children: [ // Create a ScopedModelDescendant. This widget will get the // CounterModel from the nearest ScopedModel<CounterModel>. // It will hand that model to our builder method, and rebuild // any time the CounterModel changes (i.e. after we // `notifyListeners` in the Model). new ScopedModelDescendant<CounterModel>( builder: (context, child, model) => new Text('${model.counter}'), ), new Text("Another widget that doesn't depend on the CounterModel") ]) ); } } ``` ## Finding the Model There are two ways to find the `Model` provided by the `ScopedModel` Widget. 1. Use the `ScopedModelDescendant` Widget. It will find the `Model` and run the builder function whenever the `Model` notifies the listeners. 2. Use the [`ScopedModel.of`](https://pub.dartlang.org/documentation/scoped_model/latest/) static method directly. To make this method more readable for frequent access, you can consider adding your own `of` method to your own `Model` classes like so: ```dart class CounterModel extends Model { // ... /// Wraps [ScopedModel.of] for this [Model]. static CounterModel of(BuildContext context) => ScopedModel.of<CounterModel>(context); } ``` ## Listening to multiple Models in a build function In many cases, it makes sense to split your Models apart into logical components by functionality. For example, rather than having an `AppModel` that contains all of your application logic, it can often make more sense to split models apart into a `UserModel`, a `SearchModel` and a `ProductModel`, for example. However, if you need to display information from two of these models in a single Widget, you might be wondering how to achieve that! To do so, you have two options: 1. Use multiple `ScopedModelDescendant` Widgets 2. Use multiple `ScopedModel.of` calls. No need to manage subscriptions, Flutter takes care of all of that through the magic of InheritedWidgets. ```dart class CombinedWidget extends StatelessWidget { @override Widget build(BuildContext context) { final username = ScopedModel.of<UserModel>(context, rebuildOnChange: true).username; final counter = ScopedModel.of<CounterModel>(context, rebuildOnChange: true).counter; return Text('$username tapped the button $counter times'); } } ``` ## Contributors * Original Fuchsia Authors * [Andrew Wilson](https://github.com/apwilson) * [Brian Egan](https://github.com/brianegan) * [Pascal Welsch](https://github.com/passsy)

JavaScript Libraries & Components
769 Github Stars