frideos 0.7.0+1

Frideos pub package

Helpers for state management, streams and BLoC pattern, SharedPreferences and various widgets (animations, blur, transitions, timed widgets, scrollingtext, etc.).

UPDATE (21-06-19): in the next major version will be removed the helper for the SharedPreferences package.

frideos_core pub package

  • Breaking change: it is now mandatory to import the file frideos_core/frideos_core.dart to use the StreamedObjects of the frideos_core package.

frideos_light pub package

  • A light version of this package without dependencies other than the frideos_core package, has been released as frideos_light.
1. Helpers for state management
  • AppStateModel
  • AppStateProvider
2. Widgets for streams and futures
  • ValueBuilder
  • StreamedWidget
  • ReceiverWidget
  • FuturedWidget
3. StagedObject and StagedWidget for widgets timing
  • StagedObject
  • StagedWidget
4. Widgets for effects
  • LinearTransition
  • CurvedTransition
  • FadeInWidget
  • FadeOutWidget
  • BlurWidget
  • BlurInWidget
  • BlurOutWidget
  • AnimatedBlurWidget
  • WavesWidget
5. Helper class with static methods for the SharedPreferences package
6. Various widgets
  • ScrollingText
  • HorizontalSlider
  • VerticalSlider

Dependencies #

Examples built with this library: #

  • Quiz game: a simple trivia game built with Flutter and this package. You can read an article about this example here: https://medium.com/flutter-community/flutter-how-to-build-a-quiz-game-596d0f369575

  • Todo App: an implementation of the Todo App of the Flutter Architecture Samples repository using this package.

  • Frideos examples: an example app to show how to use some features of this library.

    • Streamed objects
    • Streamed collections
    • TimerObject: a simple stopwatch
    • StagedObject
    • StagedWidget
    • AnimatedObject
    • Multiple selection and tunnel pattern (to share data between two blocs)
    • LinearTransition
    • CurvedTransition
    • Blur (fixed, in, out, animated)
    • WavesWidget
    • Sliders
    • Products catalog
  • Dynamic fields validation: a Flutter example on how to validate dynamically created fields with the BLoC pattern and this package.

  • Theme changer: a simple starter app with a drawer, app state management, dynamic theme changer and persistent theme using the sharedpreferences.

  • Counter: a simple app using the BLoC pattern showing a counter implemented with this library.

  • Blood pressure: an example of a medical app built with Flutter for the classification of the arterial blood pressure.

  • Pair game: a simple pair game (multiple selections, animations, tunnel pattern).


State management #

By extending the AppStateModel interface it is possible to create a class to drive the AppStateProvider in order to provide the data to the widgets.

From the "theme changer" example (N.B. in this "light" version of the frideos package there is no SharedPreferences helper class):

1. Create a model for the app state: #

class AppState extends AppStateModel {
  List<MyTheme> themes;
  StreamedValue<MyTheme> currentTheme;

  AppState() {
    print('-------APP STATE INIT--------');

    themes = List<MyTheme>();

    themes.addAll([
      MyTheme(
        name: 'Default',
        brightness: Brightness.light,
        backgroundColor: Colors.blue[50],
        scaffoldBackgroundColor: Colors.blue[50],
        primaryColor: Colors.blue,
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.blue[300],
      ),
      MyTheme(
        name: 'Teal',
        brightness: Brightness.light,
        backgroundColor: Colors.teal[50],
        scaffoldBackgroundColor: Colors.teal[50],
        primaryColor: Colors.teal[600],
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.teal[300],
      ),
      MyTheme(
        name: 'Orange',
        brightness: Brightness.light,
        backgroundColor: Colors.orange[50],
        scaffoldBackgroundColor: Colors.orange[50],
        primaryColor: Colors.orange[600],
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.orange[300],
      ),
    ]);

    currentTheme = StreamedValue();
  }

  void setTheme(MyTheme theme) {
    currentTheme.value = theme;
    Prefs.savePref<String>('theme', theme.name);
  }

  @override
  void init() async {
    String lastTheme = await Prefs.getPref('theme');
    if (lastTheme != null) {
      currentTheme.value = themes.firstWhere((theme) => theme.name == lastTheme,
          orElse: () => themes[0]);
    } else {
      currentTheme.value = themes[1];
    }
  }

  @override
  dispose() {
    print('---------APP STATE DISPOSE-----------');
    currentTheme.dispose();
  }
}

2. Wrap the MaterialApp in the AppStateProvider: #

void main() => runApp(App());

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  AppState appState;

  @override
  void initState() {
    super.initState();
    appState = AppState();
  }

  @override
  Widget build(BuildContext context) {
    return AppStateProvider<AppState>(
      appState: appState,
      child: MaterialPage(),
    );
  }
}

3. Consume the data: #

class MaterialPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var theme = AppStateProvider.of<AppState>(context).currentTheme;

    return ValueBuilder<MyTheme>(
        streamed: theme,
        builder: (context, snapshot) {
          return MaterialApp(
              title: "Theme and drawer starter app",
              theme: _buildThemeData(snapshot.data),
              home: HomePage());
        });
  }

  _buildThemeData(MyTheme appTheme) {
    return ThemeData(
      brightness: appTheme.brightness,
      backgroundColor: appTheme.backgroundColor,
      scaffoldBackgroundColor: appTheme.scaffoldBackgroundColor,
      primaryColor: appTheme.primaryColor,
      primaryColorBrightness: appTheme.primaryColorBrightness,
      accentColor: appTheme.accentColor,
    );
  }
}

4. Change the data (using a stream): #

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = AppStateProvider.of<AppState>(context);

    _buildThemesList() {
      return appState.themes.map((MyTheme appTheme) {
        return DropdownMenuItem<MyTheme>(
          value: appTheme,
          child: Text(appTheme.name, style: TextStyle(fontSize: 14.0)),
        );
      }).toList();
    }

    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text(
          "Settings",
        ),
      ),
      body: Container(
        padding: EdgeInsets.all(8.0),
        child: Column(
          children: <Widget>[
            Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    'Choose a theme:',
                    style: TextStyle(fontWeight: FontWeight.w500),
                  ),
                ),
                ValueBuilder<MyTheme>(
                    streamed: appState.currentTheme,
                    builder: (context, snapshot) {
                      return DropdownButton<MyTheme>(
                        hint: Text("Status"),
                        value: snapshot.data,
                        items: _buildThemesList(),
                        onChanged: appState.setTheme,
                      );
                    }),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Widgets #

ValueBuilder #

ValueBuilder extends the [StreamBuilder] widget providing some callbacks to handle the state of the stream and returning a [Container] if noDataChild is not provided, in order to avoid checking snapshot.hasData.

N.B. To use when there is no need to receive a null value.

It takes as a streamed parameter an object implementing the [StreamedObject] interface and triggers the rebuild of the widget whenever the stream emits a new event.

Usage #

ValueBuilder<String>(
  streamed: streamedValue,
  builder: (context, snasphot) => Text(snasphot.data),
  initialData: // Data to provide for the initial snapshot
  noDataChild: // Widget to show when the stream has no data
  onNoData: () => // or Callback
  errorChild: // Widget to show on error
  onError: (error) => // or Callback
)

If no [noDataChild] widget or [onNoData] callback is provided then a [Container] is returned.

If no [errorChild] widget or no [onError] callback is provided then a [Container] is returned.

N.B. The callbacks are executed only if their respective child is not provided.

StreamedWidget #

StreamedWidget extends the [StreamBuilder] widget providing some callbacks to handle the state of the stream and returning a [Container] if noDataChild is not provided, in order to avoid checking snapshot.hasData.

N.B. To use when there is no need to receive a null value.

It takes as a stream parameter a [Stream] and triggers the rebuild of the widget whenever the stream emits a new event.

If no [noDataChild] widget or [onNoData] callback is provided then a [Container] is returned.

If no [errorChild] widget or no [onError] callback is provided then a [Container] is returned.

Usage #

StreamedWidget<String>(
  stream: stream,
  builder: (context, snasphot) => Text(snasphot.data),
  noDataChild: // Widget to show when the stream has no data
  onNoData: () => // or Callback
  errorChild: // Widget to show on error
  onError: (error) => // or Callback
)

In case of an object implementing the StreamedObject interface (eg. StreamedValue, StreameList etc.):

StreamedWidget<String>(
  stream: streamedObject.outStream, // outStream getter
  builder: (context, snasphot) => Text(snasphot.data),
  noDataChild: // Widget to show when the stream has no data
  onNoData: () => // or Callback
  errorChild: // Widget to show on error
  onError: (error) => // or Callback
)

N.B. The callbacks are executed only if their respective child is not provided.

ReceiverWidget #

Used with a StreamedValue when the type is a widget to directly stream a widget to the view. Under the hood a StreamedWidget handles the stream and shows the widget.

Usage #

ReceiverWidget(stream: streamedValue.outStream),

FuturedWidget #

FuturedWidget is a wrapper for the [FutureBuilder] widget. It provides some callbacks to handle the state of the future and returning a [Container] if onWaitingChild is not provided, in order to avoid checking snapshot.hasData.

Usage #

FuturedWidget<String>(
  future: future,
  builder: (context, snasphot) => Text(snasphot.data),
  initialData: // Data to provide if the snapshot is null or still not completed
  waitingChild: // Widget to show on waiting
  onWaiting: () => // or Callback
  errorChild: // Widget to show on error
  onError: (error) => // or Callback
)

If no [onWaitingChild] widget or [onWaiting] callback is provided then a [Container] is returned.

If no [errorChild] widget or no [onError] callback is provided then a [Container] is returned.

N.B. The callbacks are executed only if their respective child is not provided.

Widgets timing #

StagedObject #

A complex class to hadle the rendering of widgets over the time. It takes a collection of "Stages" and triggers the visualization of the widgets at a given time (relative o absolute timing). For example to make a demostration on how to use an application, showing the widgets and pages along with explanations.

StagedObject

Every stage is handled by using the Stage class:

class Stage {
  Widget widget;
  int time; // milliseconds
  Function onShow = () {};
  Stage({this.widget, this.time, this.onShow});
}
N.B. The onShow callback is used to trigger an action when the stage shows #

Usage #

From the StagedObject example:

  1. Declare a map <int, Stage>

    Here the map is in the view and is set in the BLoC class by the setStagesMap.
Map<int, Stage> get stagesMap => <int, Stage>{
  0: Stage(
      widget: Container(
        width: 200.0,
        height: 200.0,
        color: Colors.indigo[200],
        alignment: Alignment.center,
        key: Key('0'),
        child: ScrollingText(
          text:
            'This stage will last 8 seconds. By the onShow call back it is possibile to assign an action when the widget shows.',
          scrollingDuration: 2000,
          style: TextStyle(
            color: Colors.blue,
            fontSize: 18.0,
            fontWeight: FontWeight.w500)),
        ),
      time: 8000,
      onShow: () {}),
  1: Stage(
      widget: Container(
        width: 200.0,
        height: 200.0,
        color: Colors.indigo[200],
        alignment: Alignment.center,
        key: Key('00'),
        child: ScrollingText(
              text: 'The next widgets will cross      fade.',
              scrollingDuration: 2000,
            ),
          ),
      time: 8000,
      onShow: () {}),

}
  1. In the BLoC #

  final text = StreamedValue<String>();
  final staged = StagedObject();


  // The map can be set through the constructor of the StagedObject
  // or by the setStagesMap method like in this case.
  setMap(Map<int, Stage> stagesMap) {
    staged.setStagesMap(stagesMap);
  }


  // This method is then called from a button in the view
  start() {
    if (staged.getMapLength() > 0) {
      staged.setCallback(sendNextStageText);
      staged.startStages();
    }
  }

  // By this method we get the next stage to show it
  // in a little box below the current stage
  sendNextStageText() {
    var nextStage = staged.getNextStage();
    if (nextStage != null) {
      text.value = "Next stage:";
      widget.value = nextStage.widget;
      stage.value = StageBridge(
          staged.getStageIndex(), staged.getCurrentStage(), nextStage);
    } else {
      text.value = "This is the last stage";
      widget.value = Container();
    }
  }

  1. In the view: #

  // Setting the map in the build method
  StagedObjectBloc bloc = BlocProvider.of(context);
  bloc.setMap(stagesMap);


  // To show the current widget on the view using the ReceiverWidget.
  // As an alternative it can be used the StreamedWidget/StreamBuilder.
  ReceiverWidget(
    stream: bloc.staged.widgetStream,
  ),

StagedWidget #

StagedWidget

Usage #

  1. Declare a map <int, Stage>

    Here the map is in the view and is set in the BLoC class by the setStagesMap.
Map<int, Stage> get stagesMap => <int, Stage>{
  0: Stage(
      widget: Container(
        width: 200.0,
        height: 200.0,
        color: Colors.indigo[200],
        alignment: Alignment.center,
        key: Key('0'),
        child: ScrollingText(
          text:
            'This stage will last 8 seconds. By the onShow call back it is possibile to assign an action when the widget shows.',
          scrollingDuration: 2000,
          style: TextStyle(
            color: Colors.blue,
            fontSize: 18.0,
            fontWeight: FontWeight.w500)),
        ),
      time: 8000,
      onShow: () {}),
  1: Stage(
      widget: Container(
        width: 200.0,
        height: 200.0,
        color: Colors.indigo[200],
        alignment: Alignment.center,
        key: Key('00'),
        child: ScrollingText(
              text: 'The next widgets will cross      fade.',
              scrollingDuration: 2000,
            ),
          ),
      time: 8000,
      onShow: () {}),

}
  1. In the view: #

StagedWidget(
  stagesMap: stagesMap,
  onStart: // function to call,
  onEnd: () {
    // Function to call at the end of the last stage
    // (only if relative timing):
    // e.g. Navigator.pop(context);
  }),

Effects #

LinearTransition #

Linear cross fading transition between two widgets, it can be used with the StagedObject.

LinearTransition

Usage #

LinearTransition(
  firstWidget: Container(height: 100.0, width: 100.0,
        color: Colors.blue),
  secondWidget: Container(height: 100.0, width: 100.0,
        color: Colors.lime),
  transitionDuration: 4000,
),

CurvedTransition #

Cross fading transition between two widgets. This uses the Flutter way to make an animation.

CurvedTransition

Usage #

CurvedTransition(
  firstWidget: Container(height: 100.0, width: 100.0,
     color: Colors.blue),
  secondWidget: Container(height: 100.0, width: 100.0,
     color: Colors.lime),
  transitionDuration: 4000,
  curve: Curves.bounceInOut,
),

FadeInWidget #

Usage #

FadeInWidget(
  duration: 7000,
  child: ScrollingText(
      text: 'Fade in text',
      scrollingDuration: 2000,
      style: TextStyle(
        color: Colors.blue,
        fontSize: 94.0,
        fontWeight: FontWeight.w500,
      ),
    ),
),

FadeOutWidget #

Usage #

FadeOutWidget(
  duration: 7000,
  child: ScrollingText(
      text: 'Fade out text',
      scrollingDuration: 2000,
      style: TextStyle(
        color: Colors.blue,
        fontSize: 94.0,
        fontWeight: FontWeight.w500,
      ),
    ),
),

BlurWidget #

Blur

Usage #

BlurWidget(
  sigmaX: 2.0,
  sigmaY: 3.0,
  child: Text('Fixed blur')
)

BlurInWidget #

Usage #

BlurInWidget(
  initialSigmaX: 2.0,
  initialSigmaY: 12.0,
  duration: 5000,
  refreshTime: 20,
  child: Text('Blur out'),
)

BlurOutWidget #

Usage #

BlurOutWidget(
  finalSigmaX: 2.0,
  finalSigmaY: 12.0,
  duration: 5000,
  refreshTime: 20,
  child: Text('Blur out'),
)

AnimatedBlurWidget #

Usage #

AnimatedBlurWidget(
  initialSigmaX: 2.0,
  initialSigmaY: 3.0,
  finalSigmaX: 2.0,
  finalSigmaY: 3.0,
  duration: 5000,
  reverseAnimation: true,
  loop: true,
  refreshTime: 20,
  child: Text('Fixed blur')
)

WavesWidget #

Usage #

WavesWidget(
  width: 128.0,
  height: 128.0,
  color: Colors.red,
  child: Container(
    color: Colors.red[400],
 ),

SharedPreferences helper #

- savePrefs(String Key, T value) #

- saveStringList(String Key, List

- getPref(String key) #

- getKeys() #

- remove(String key) #

From the "Theme changer" example:

- Save the theme choosed so that the next time it will be set on startup #
void setTheme(MyTheme theme) {
  currentTheme.value = theme;
  Prefs.savePref<String>('apptheme', theme.name);
}
- Load the theme when the app starts #
@override
void init() async {
  String lastTheme = await Prefs.getPref('apptheme');
  if (lastTheme != null) {
    currentTheme.value =
      themes.firstWhere((theme) => theme.name == lastTheme, orElse: () => themes[0]);
  } else {
    currentTheme.value = themes[0];
  }
}

Various #

ScrollingText #

Usage #

ScrollingText(
 text: 'Text scrolling (during 8 seconds).',
 scrollingDuration: 2000, // in milliseconds
 style: TextStyle(color: Colors.blue,
    fontSize: 18.0, fontWeight: FontWeight.w500),
),

Sliders #

Sliders

Usage #

HorizontalSlider(
  key: _horizontalSliderKey,
  rangeMin: 0.0,
  rangeMax: 3.14,
  //step: 1.0,
  initialValue: bloc.initialAngle,
  backgroundBar: Colors.indigo[50],
  foregroundBar: Colors.indigo[500],
  triangleColor: Colors.red,
  onSliding: (slider) {
    bloc.horizontalSlider(slider);
  },
)



VerticalSlider(
  key: _verticalSliderKey,
  rangeMin: 0.5,
  rangeMax: 5.5,
  step: 1.0, // Default value 1.0
  initialValue: bloc.initialScale,
  backgroundBar: Colors.indigo[50],
  foregroundBar: Colors.indigo[500],
  triangleColor: Colors.red,
  onSliding: (slider) {
    bloc.verticalSlider(slider);
  },
)

Version 0.7.0+1 (21-06-19) #

  • Updated to frideos_core 0.5.0
  • Updated to RxDart 0.22.0

Version 0.6.2 (08-05-19) #

  • Added the parameter initAppState (set to true by default) to the AppStateProvider widget. Setting it to false the init method of the AppStateModel derived class passed to the appState parameter won't be called to the initState method of the AppStateProvider.
  • Bugfix to the Blur widgets.
  • Update README.md: added a Dynamic fields validation example.
  • Updated to frideos_core 0.4.4.

Version 0.6.1 (13-04-19) #

  • Update README.md: added a new example.

  • Bugfix to the LinearTransition widget.

Version 0.6.0 (31-03-19) #

  • Breaking change: integrated the frideos_core package. It now mandatory to import the file frideos_core/frideos_core.dart to use the StreamedObjects.

  • Updated the README.md.

  • Updated dependency to RxDart version ^0.21

Version 0.5.0+1 (23-03-19) #

  • Update the README.md: added a new example app (a Todo app).

  • Package refactoring: in the example folder there is now a simple example of a counter implemented with this library (the previous examples in this repository).

  • Added some unit tests.

Version 0.5.0 (21-03-19) #

  • Breaking change: the name of the parameter stream of the ValueBuilder widget was changed to streamed to highlight this is intended to use with the classes that implement the StreamedObject interface.
  • Code refactoring
  • Tests updated
  • README.md updated

Version 0.4.2 (16-03-19) #

  • Code refactoring
  • Update Readme.md

Version 0.4.1 (10-03-19) #

- StreamedList #

  • Added AddAll method.

- AnimatedObject #

  • added AnimatedType enum, to handle the behavior of the animated object.

  • added startAnimation method: it is now possible specify a type of behavior (increment or decrement the value), the velocity, a minValue (in case of decrement) and a maxValue (increment).

  • added outStream getter (deprecated: animationStream).

  • Code refactoring.

- Blur widgets and Wave widget refactored to use the ValueBuilder widget. #

- Code refactoring. #

Version 0.4.0+3 (08-03-19) #

  • Docs improved
  • Code refactoring

Version 0.4.0 (03-03-19) #

BREAKING CHANGES #

Due to various changes in the code this version could cause issues on apps based on a previous version. Now, all the StreamedObjects need to have passed their value to the initialData parameter of the StreamBuilder/StreamedWidget (e.g. using the getter value of the StreamedObjects). As an alternative, they can be used with the new ValueBuilder widget, it extends the StreamBuilder class and get the value from the StreamedObject to pass to the initialData parameter.

- AppStateModel #

By extending the AppStateModel interface it is possible to create a class to drive the AppStateProvider in order to provide the data to the widgets.

- AppStateProvider #

Simple state provider that extends a StatefulWidget and use an InheritedWidget to share the state with the widgets on the tree. Used along with streams, it is possibile for the widgets the access this data to modify it and propagates the changes on the entire widgets tree.

From the "theme changer" example:

1. Create a model for the app state: #

class AppState extends AppStateModel {
  List<MyTheme> themes;
  StreamedValue<MyTheme> currentTheme;

  AppState() {
    print('-------APP STATE INIT--------');

    themes = List<MyTheme>();

    themes.addAll([
      MyTheme(
        name: 'Default',
        brightness: Brightness.light,
        backgroundColor: Colors.blue[50],
        scaffoldBackgroundColor: Colors.blue[50],
        primaryColor: Colors.blue,
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.blue[300],
      ),
      MyTheme(
        name: 'Teal',
        brightness: Brightness.light,
        backgroundColor: Colors.teal[50],
        scaffoldBackgroundColor: Colors.teal[50],
        primaryColor: Colors.teal[600],
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.teal[300],
      ),
      MyTheme(
        name: 'Orange',
        brightness: Brightness.light,
        backgroundColor: Colors.orange[50],
        scaffoldBackgroundColor: Colors.orange[50],
        primaryColor: Colors.orange[600],
        primaryColorBrightness: Brightness.dark,
        accentColor: Colors.orange[300],
      ),
    ]);

    currentTheme = StreamedValue();
  }

  void setTheme(MyTheme theme) {
    currentTheme.value = theme;
    Prefs.savePref<String>('theme', theme.name);
  }

  @override
  void init() async {    
    String lastTheme = await Prefs.getPref('theme');
    if (lastTheme != null) {
      currentTheme.value =
          themes.firstWhere((theme) => theme.name == lastTheme);
    } else {
      currentTheme.value = themes[1];
    }
  }

  @override
  dispose() {
    print('---------APP STATE DISPOSE-----------');
    currentTheme.dispose();
  }
}

2. Wrap the MaterialApp in the AppStateProvider: #

void main() => runApp(App());

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  AppState appState;

  @override
  void initState() {
    super.initState();
    appState = AppState();
  }

  @override
  Widget build(BuildContext context) {
    return AppStateProvider<AppState>(
      appState: appState,
      child: MaterialPage(),
    );
  }
}

3. Consume the data: #

class MaterialPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var theme = AppStateProvider.of<AppState>(context).currentTheme;

    return ValueBuilder<MyTheme>(
        stream: theme,
        builder: (context, snapshot) {
          return MaterialApp(
              title: "Theme and drawer starter app",
              theme: _buildThemeData(snapshot.data),                  
              home: HomePage());
        });
  }

  _buildThemeData(MyTheme appTheme) {
    return ThemeData(
      brightness: appTheme.brightness,
      backgroundColor: appTheme.backgroundColor,
      scaffoldBackgroundColor: appTheme.scaffoldBackgroundColor,
      primaryColor: appTheme.primaryColor,
      primaryColorBrightness: appTheme.primaryColorBrightness,
      accentColor: appTheme.accentColor,
    );
  }
}

4. Change the data (using a stream): #

class SettingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = AppStateProvider.of<AppState>(context);

    _buildThemesList() {
      return appState.themes.map((MyTheme appTheme) {
        return DropdownMenuItem<MyTheme>(
          value: appTheme,
          child: Text(appTheme.name, style: TextStyle(fontSize: 14.0)),
        );
      }).toList();
    }

    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: Text(
          "Settings",
        ),
      ),
      body: Container(
        padding: EdgeInsets.all(8.0),
        child: Column(
          children: <Widget>[
            Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    'Choose a theme:',
                    style: TextStyle(fontWeight: FontWeight.w500),
                  ),
                ),         
                ValueBuilder<MyTheme>(
                    stream: appState.currentTheme,
                    builder: (context, snapshot) {
                      return DropdownButton<MyTheme>(
                        hint: Text("Status"),
                        value: snapshot.data,
                        items: _buildThemesList(),
                        onChanged: appState.setTheme,
                      );
                    }),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

- ValueBuilder widget #

Similar to the StreamedWidget, it extends the StreamBuilder class but takes as a stream parameter an object that implements the StreamedObject interface.

From the previous example:

ValueBuilder<MyTheme>(
  stream: theme,        
  builder: (context, snapshot) {
    return MaterialApp(
      title: "Theme and drawer starter app",
      theme: _buildThemeData(snapshot.data),
      home: HomePage(),
    );
  }
)

- Helper class with static metods for the SharedPreferences package #

1. savePrefs(String Key, T value) #

2. saveStringList(String Key, List

3. getPref(String key) #

3. getKeys() #

3. remove(String key) #

StreamedObjects #

- Added the onChange method. It calls a function every time the stream updates.

- More details in the debugMode. #

- Code refactoring #

Version 0.3.0 (24-02-19) #

- StreamedList and StreamedMap classes #

Breaking change:

StreamedList and StreamedMap by default aren't initialiazed (to avoid that when using them along with a StreamBuilder/StreamedWidget the snaphost.hasData is true from the beginning,becoming harder to show for example a loading spinner without using a workaround).

- "initialData" parameter #

  • Added initialData parameter to the constructor to all the streamed classes to initialize the stream with an initial data.

- Code refactoring #

Version 0.2.0 (22-02-19) #

- StreamedList class #

Methods added: #
  • replaceAt
  • replace

Version 0.1.3 (11-02-19) #

- Changed console messages behavior and added debugMode method.

By default, the debug console messages on disposing of streams are now disabled. Use this method to reenable this behavior.

e.g. from the "StreamedObjects" example:

    // Activate the debug console messages on disposing
    count.debugMode();
    countMemory.debugMode();
    countHistory.debugMode();
    timerObject.debugMode();
    counterObj.debugMode();

This will be applied to:

  • StreamedValue
  • StreamedTransformed
  • MemoryObject
  • HistoryObject
  • StreamedList
  • StreamedMap

Version 0.1.2 (10-02-19) #

AnimatedObject #

- Added getter and setter for the value of the AnimatedObject: #

Intead of animateObject.animation.value, now the current value of the animation it is accessible just by using the 'value' setter/getter:

animatedObject.value += 0.1;
// It is the same as animatedObject.animation.value += 0.1

AnimatedObject example updated #

- Added multiple rotations #

Blur widgets #

- Code refactoring #

Version 0.1.1 (29-01-19) #

- Documentation improved #

- Code refactoring #

- New widgets: #

  • BlurWidget
  • BlurInWidget
  • BlurOutWidget
  • AnimatedBlurWidget
  • WavesWidget

Version 0.1.0 (26-01-19) #

- Released as package #

- Code refactoring #

Version 0.0.3 (24-01-19) #

- Added the FadeInWidget #

- Added the FadeOutWidget #

- Added the StagedWidget #

- StagedObject class #

1. Added the onEnd callback
2. The widgetsMap property was renamed to stagesMap

Version 0.0.2 #

- StreamedList class #

1. Added a getter for the length of the list #

e.g. streamedList.length

it is the same as: streamedList.value.length

2. Methods added: #
  • removeElement
  • removeAt
  • clear

- StreamedMap class #

1. Added a getter for the length of the map #

e.g. streamedMap.length

it is the same as: streamedMap.value.length

2. Methods added: #
  • AddKey
  • removeKey
  • clear

example/lib/main.dart

import 'package:flutter/material.dart';

import 'package:frideos_core/frideos_core.dart';
import 'package:frideos/frideos.dart';

void main() => runApp(MyApp());

class AppState extends AppStateModel {
  factory AppState() => _singletonAppState;

  AppState._internal();

  static final AppState _singletonAppState = AppState._internal();

  // STREAM
  final counter = StreamedValue<int>();

  void incrementCounter() {
    counter.value++;
  }

  @override
  void init() {
    counter.value = 0;
  }

  @override
  void dispose() {
    counter.dispose();
  }
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  AppState appState;

  @override
  void initState() {
    super.initState();
    appState = AppState();
  }

  @override
  void dispose() {
    appState.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AppStateProvider<AppState>(
      appState: appState,
      child: MaterialApp(
        title: 'Counter',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appState = AppStateProvider.of<AppState>(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Main page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('Counter:'),
                Container(width: 30),
                ValueBuilder<int>(
                  streamed: appState.counter,
                  builder: (context, snapshot) => Text('${snapshot.data}'),
                  noDataChild: const Text('null'),
                ),
              ],
            ),
            Container(height: 30),
            RaisedButton(
                child: const Text('Second page'),
                onPressed: () {
                  Navigator.push(context,
                      MaterialPageRoute(builder: (context) => SecondPage()));
                }),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: appState.incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final appState = AppStateProvider.of<AppState>(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Other page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('Counter:'),
                Container(width: 30),
                ValueBuilder<int>(
                  streamed: appState.counter,
                  builder: (context, snapshot) => Text('${snapshot.data}'),
                  noDataChild: const Text('null'),
                ),
              ],
            ),
            Container(height: 30),
            RaisedButton(
                child: const Text('Other page'),
                onPressed: () {
                  Navigator.push(context,
                      MaterialPageRoute(builder: (context) => SecondPage()));
                }),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: appState.incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  frideos: ^0.7.0+1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:frideos/frideos.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
88
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
94
Learn more about scoring.

We analyzed this package on Jul 17, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.4.0
  • pana: 0.12.19
  • Flutter: 1.7.8+hotfix.3

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Health suggestions

Format lib/src/effects/blur.dart.

Run flutter format to format lib/src/effects/blur.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
flutter 0.0.0
frideos_core ^0.5.0 0.5.0+1
rxdart ^0.22.0 0.22.0
shared_preferences ^0.5.1+1 0.5.3+4
Transitive dependencies
collection 1.14.11
meta 1.1.6 1.1.7
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test
test ^1.5.1

Admin