frideos 0.4.2 copy "frideos: ^0.4.2" to clipboard
frideos: ^0.4.2 copied to clipboard

outdated

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

Frideos pub package #

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

frideos_core and frideos_light

  • In next releases, the core of the library will be replaced by the new frideos_core package, so it will become mandatory for the apps based on the frideos package to add it as a dependency.

  • A light version of this package without dependencies other than the frideos_core package, has been released as frideos_light.

Helpers for state management

  • AppStateModel
  • AppStateProvider

Helpers for streams and BLoC pattern:

  • StreamedValue
  • StreamedTransformed
  • StreamedList
  • StreamedMap
  • MemoryValue
  • HistoryObject
  • StreamedSender

Helpers for tunnel pattern

  • StreamedSender
  • ListSender
  • MapSender

Helper class with static methods for the SharedPreferences package

Widgets for streams and futures

  • ValueBuilder
  • StreamedWidget
  • ReceiverWidget
  • FuturedWidget

Classes for animations and timing

  • TimerObject
  • AnimatedObject
  • StagedObject
  • StagedWidget

Widgets for effects

  • LinearTransition
  • CurvedTransition
  • FadeInWidget
  • FadeOutWidget
  • BlurWidget
  • BlurInWidget
  • BlurOutWidget
  • AnimatedBlurWidget
  • WavesWidget

Various widgets

  • ScrollingText
  • HorizontalSlider
  • VerticalSlider

Examples built with this library

  • Example app
  • Theme changer and persistent theme with SharedPreferences
  • Counter app
  • Blood pressure example app
  • Pair game

Dependencies #

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:

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,
                      );
                    }),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Streams and BLoC pattern #

Utility classes to make a little bit easier working with streams.

This example (you can find it in the example folder of this repo) shows how to use some classes of this library, and a comparison code without it. It is just a page with two textfields to add a key/value pair to a map. The map is then used to drive a ListView.builder showing all the pairs.

Common code

class Validators {
  final validateText =
      StreamTransformer<String, String>.fromHandlers(handleData: (str, sink) {
    if (str.isNotEmpty) {
      sink.add(str);
    } else {
      sink.addError('The text must not be empty.');
    }
  });

  final validateKey =
      StreamTransformer<String, int>.fromHandlers(handleData: (key, sink) {
    var k = int.tryParse(key);
    if (k != null) {
      sink.add(k);
    } else {
      sink.addError('The key must be an integer.');
    }
  });
}
  1. BLoC without this library
class StreamedMapCleanBloc extends BlocBase with Validators {
  StreamedMapCleanBloc() {
    print('-------StreamedMapClean BLOC--------');
  }

  final _map = BehaviorSubject<Map<int, String>>();
  Stream<Map<int, String>> get outMap => _map.stream;
  Function(Map<int, String> map) get inMap => _map.sink.add;
  final map = Map<int, String>();

  final _text = BehaviorSubject<String>();
  Stream<String> get outText => _text.stream;
  Stream<String> get outTextTransformed => _text.stream.transform(validateText);
  Function(String text) get inText => _text.sink.add;

  final _key = BehaviorSubject<String>();
  Stream<String> get outKey => _key.stream;
  Stream<int> get outKeyTransformed => _key.stream.transform(validateKey);
  Function(String) get inKey => _key.sink.add;

  Observable<bool> get isFilled => Observable.combineLatest2(
      outTextTransformed, outKeyTransformed, (a, b) => true);

  // Add to the streamed map the key/value pair put by the user
  void addText() {
    final key = int.parse(_key.value);
    final value = _text.value;
    final streamMap = _map.value;

    if (streamMap != null) {
      map.addAll(streamMap);
    }

    map[key] = value;
    inMap(map);
  }

  @override
  void dispose() {
    print('-------StreamedMapClean BLOC DISPOSE--------');

    _map.close();
    _text.close();
    _key.close();
  }
}
  1. With this library:
class StreamedMapBloc extends BlocBase with Validators {
  StreamedMapBloc() {
    print('-------StreamedMap BLOC--------');

    // Set the validation transformers for the textfields
    streamedText.setTransformer(validateText);
    streamedKey.setTransformer(validateKey);

    // Activate the debug console messages on disposing
    streamedMap.debugMode();
    streamedText.debugMode();
    streamedKey.debugMode();
  }

  final streamedMap = StreamedMap<int, String>(initialData: Map());
  final streamedText = StreamedTransformed<String, String>();
  final streamedKey = StreamedTransformed<String, int>();

  Observable<bool> get isFilled => Observable.combineLatest2(
      streamedText.outTransformed, streamedKey.outTransformed, (a, b) => true);

  // Add to the streamed map the key/value pair put by the user
  void addText() {
    final key = int.parse(streamedKey.value);
    final value = streamedText.value;

    streamedMap.addKey(key, value);

    // Or, as an alternative:
    //streamedMap.value[key] = value;
    //streamedMap.refresh();
  }

  @override
  void dispose() {
    print('-------StreamedMap BLOC DISPOSE--------');
    streamedMap.dispose();
    streamedText.dispose();
    streamedKey.dispose();
  }
}

As you can see the code is more clean, easier to read and to mantain.

StreamedValue #

It's the simplest class that implements the [StreamedObject] interface.

Every time a new value is set, this is compared to the oldest one and if it is different, it is sent to stream.

Used in tandem with [ValueBuilder] it automatically triggers the rebuild of the widgets returned by its builder.

So for example, instead of:

counter += 1;
stream.sink.add(counter);

It becomes just:

counter.value += 1;

It can be used even with [StreamedWidget] and [StreamBuilder] by using its stream getter outStream. In this case, it is necessary to pass to the initialData parameter the current value of the [StreamedValue] (e.g. using the getter value).

N.B. when the type is not a basic type (e.g int, double, String etc.) and the value of a property of the object is changed, it is necessary to call the refresh method to update the stream.

Usage

// In the BLoC
final count = StreamedValue<int>(initialData: 0);

incrementCounter() {
  count.value += 2.0;
}

// View
ValueBuilder<int>(
  stream: bloc.count, // no need of the outStream getter with ValueBuilder
  builder: (context, snapshot) =>
    Text('Value: ${snapshot.data}'),
  noDataChild: Text('NO DATA'),
),
RaisedButton(
    color: buttonColor,
    child: Text('+'),
    onPressed: () {
      bloc.incrementCounter();
    },
),

// As an alternative:
//
// StreamedWidget<int>(
//    initialData: bloc.count.value
//    stream: bloc.count.outStream,
//    builder: (context, snapshot) => Text('Value: ${snapshot.data}'),
//    noDataChild: Text('NO DATA'),
//),

On update the [timesUpdated] increases showing how many times the value has been updated.

N.B. For collections use [StreamedList] and [StreamedMap] instead.

StreamedTransformed #

A particular class the implement the StreamedObject interface, to use when there is the need of a StreamTransformer (e.g. stream transformation, validation of input fields, etc.).

Usage

From the StreamedMap example:

// In the BLoC class
final streamedKey = StreamedTransformed<String, int>();



// In the constructor of the BLoC class
streamedKey.setTransformer(validateKey);



// Validation (e.g. in the BLoC or in a mixin class)
final validateKey =
      StreamTransformer<String, int>.fromHandlers(handleData: (key, sink) {
    var k = int.tryParse(key);
    if (k != null) {
      sink.add(k);
    } else {
      sink.addError('The key must be an integer.');
    }
  });


// In the view:
StreamBuilder<int>(
            stream: bloc.streamedKey.outTransformed,
            builder: (context, snapshot) {
              return Column(
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.symmetric(
                      vertical: 12.0,
                      horizontal: 20.0,
                    ),
                    child: TextField(
                      style: TextStyle(
                        fontSize: 18.0,
                        color: Colors.black,
                      ),
                      decoration: InputDecoration(
                        labelText: 'Key:',
                        hintText: 'Insert an integer...',
                        errorText: snapshot.error,
                      ),
                      // To avoid the user could insert text use the TextInputType.number
                      // Here is commented to show the error msg.
                      //keyboardType: TextInputType.number,
                      onChanged: bloc.streamedKey.inStream,
                    ),
                  ),
                ],
              );
            }),

StreamedList #

This class has been created to work with lists. It works like [StreamedValue].

To modify the list (e.g. adding items) and update the stream automatically use these methods:

  • [AddAll]
  • [addElement]
  • [clear]
  • [removeAt]
  • [removeElement]
  • [replace]
  • [replaceAt]

For other direct actions on the list, to update the stream call the [refresh] method instead.

Usage

e.g. adding an item:

 streamedList.addElement(item);

it is the same as:

  streamedList.value.add(item);
  streamedList.refresh();

From the StreamedList example:

  final streamedList = StreamedList<String>();


  // Add to the streamed list the string from the textfield
  addText() {
    streamedList.addElement(streamedText.value);

    // Or, as an alternative:
    // streamedList.value.add(streamedText.value);
    // streamedList.refresh(); // To refresh the stream with the new value
  }

StreamedMap #

This class has been created to work with maps, it works like [StreamedList].

To modify the list (e.g. adding items) and update the stream automatically use these methods:

  • [addKey]
  • [removeKey]
  • [clear]

For other direct actions on the map, to update the stream call the [refresh] method instead.

Usage

e.g. adding a key/value pair:

  streamedMap.addKey(1, 'first');

it is the same as:

   streamedMap.value[1] = 'first';
   streamedList.refresh();

From the streamed map example:

  final streamedMap = StreamedMap<int, String>();


  // Add to the streamed map the key/value pair put by the user
  addText() {
    var key = int.parse(streamedKey.value);
    var value = streamedText.value;

    streamedMap.addKey(key, value);

    // Or, as an alternative:
    //streamedMap.value[key] = value;
    //streamedMap.refresh();
  }

MemoryValue #

The MemoryObject has a property to preserve the previous value. The setter checks for the new value, if it is different from the one already stored, this one is given [oldValue] before storing and streaming the new one.

Usage

final countMemory = MemoryValue<int>();

countMemory.value // current value
couneMemory.oldValue // previous value

HistoryObject #

Extends the [MemoryValue] class, adding a [StreamedCollection]. Useful when it is need to store a value in a list.

final countHistory = HistoryObject<int>();

incrementCounterHistory() {
  countHistory.value++;
}

saveToHistory() {
  countHistory.saveValue();
}

Tunnel pattern #

Easy pattern to send data from one BLoC to another one.

StreamedSender #

Used to make a one-way tunnel beetween two blocs (from blocA to a StremedValue on blocB).

Usage

  1. Define an object that implements the StreamedObject interface in the blocB (e.g. a StreamedValue):

final receiverStr = StreamedValue<String>();
  1. Define a StreamedSender in the blocA:

final tunnelSenderStr = StreamedSender<String>();
  1. Set the receiver in the sender on the class the holds the instances of the blocs:

blocA.tunnelSenderStr.setReceiver(blocB.receiverStr);
  1. To send data from blocA to blocB then:

tunnelSenderStr.send("Text from blocA to blocB");

ListSender and MapSender #

Like the StreamedSender, but used with collections.

Usage

  1. Define a StreamedList or StreamedMap object in the blocB

final receiverList = StreamedList<int>();
final receiverMap = StreamedMap<int, String>();
  1. Define a ListSender/MapSender in the blocA

final tunnelList = ListSender<int>();
final tunnelMap = MapSender<int, String>();
  1. Set the receiver in the sender on the class the holds the instances of the blocs

blocA.tunnelList.setReceiver(blocB.receiverList);
blocA.tunnelMap.setReceiver(blocB.receiverMap);
  1. To send data from blocA to blocB then:

tunnelList.send(list);
tunnelMap.send(map);

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];
  }
}

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 stream parameter an object implementing the [StreamedObject] interface and triggers the rebuild of the widget whenever the stream emits a new event.

Usage

ValueBuilder<String>(
  stream: streamedValue,
  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
)

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),
  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.

Animations and timing #

TimerObject #

An object that embeds a timer and a stopwatch.

Usage

final timerObject = TimerObject();

startTimer() {
  timerObject.startTimer();
}

stopTimer() {
  timerObject.stopTimer();
}

getLapTime() {
  timerObject.getLapTime();
}

incrementCounter(Timer t) {
  counter.value += 2.0;
}

startPeriodic() {
   var interval = Duration(milliseconds: 1000);
   timerObject.startPeriodic(interval, incrementCounter);
}

AnimatedObject #

This class is used to update a value over a period of time. Useful to handle animations using the BLoC pattern.

From the AnimatedObject example:

AnimatedObject

Usage

  • In the BLoC:

// Initial value 0.5, updating interval 20 milliseconds
  final scaleAnimation =
      AnimatedObject<double>(initialValue: 0.5, interval: 20);


  final rotationAnimation =
      AnimatedObject<double>(initialValue: 0.5, interval: 20);

  start() {
    scaleAnimation.start(updateScale);
    rotationAnimation.start(updateRotation);
  }

  updateScale(Timer t) {
    scaleAnimation.value += 0.03;

    if (scaleAnimation.value > 8.0) {
      scaleAnimation.reset();
    }
  }

  updateRotation(Timer t) {
    rotationAnimation.value += 0.1;
  }


  stop() {
    scaleAnimation.stop();
    rotationAnimation.stop();
  }

  reset() {
    scaleAnimation.reset();
    rotationAnimation.reset();
  }
  • In the view:

      Container(
          color: Colors.blueGrey[100],
          child: Column(
            children: <Widget>[
              Container(height: 20.0,),
               ValueBuilder<AnimatedStatus>(
                stream: bloc.scaleAnimation.status,
                builder: (context, snapshot) {
                  return Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      snapshot.data == AnimatedStatus.active
                          ? RaisedButton(
                              color: Colors.lightBlueAccent,
                              child: Text('Reset'),
                              onPressed: () {
                                bloc.reset();
                              })
                          : Container(),
                      snapshot.data == AnimatedStatus.stop
                          ? RaisedButton(
                              color: Colors.lightBlueAccent,
                              child: Text('Start'),
                              onPressed: () {
                                bloc.start();
                              })
                          : Container(),
                      snapshot.data == AnimatedStatus.active
                          ? RaisedButton(
                              color: Colors.lightBlueAccent,
                              child: Text('Stop'),
                              onPressed: () {
                                bloc.stop();
                              })
                          : Container(),
                    ],
                  );
                },
              ),
              Expanded(
                child: ValueBuilder<double>(
                    stream: bloc.scaleAnimation,
                    builder: (context, snapshot) {
                      return Transform.scale(
                          scale: snapshot.data,
                          // No need for StreamBuilder here, the widget
                          // is already updating
                          child: Transform.rotate(
                              angle: bloc.rotationAnimation.value,
                              // Same here
                              //
                              child: Transform(
                                  transform: Matrix4.rotationY(
                                      bloc.rotationAnimation.value),
                                  child: FlutterLogo())));
                    }),
              )
            ],
          ),
        ),

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],
 ),

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);
  },
)

Examples #

1. General #

An example app to show how to use 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

2. Theme changer #

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

3. Counter #

A simple app using the BLoC pattern showing a counter implemented with this library.

4. Blood pressure #

An example of a medical app built with Flutter for the classification of the arterial blood pressure.

5. Pair game #

A simple pair game (multiple selections, animations, tunnel pattern).

41
likes
0
pub points
72%
popularity

Publisher

unverified uploader

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

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter, rxdart, shared_preferences

More

Packages that depend on frideos