frideos 0.5.0+1 copy "frideos: ^0.5.0+1" to clipboard
frideos: ^0.5.0+1 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 pub package

  • In next release, the core of the library will be replaced by the new 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. Helpers for streams and BLoC pattern:
  • StreamedValue
  • StreamedTransformed
  • StreamedList
  • StreamedMap
  • MemoryValue
  • HistoryObject
  • StreamedSender
3. Helpers for tunnel pattern
  • StreamedSender
  • ListSender
  • MapSender
4. Widgets for streams and futures
  • ValueBuilder
  • StreamedWidget
  • ReceiverWidget
  • FuturedWidget
5. Classes for animations and timing
  • TimerObject
  • AnimatedObject
  • StagedObject
  • StagedWidget
6. Widgets for effects
  • LinearTransition
  • CurvedTransition
  • FadeInWidget
  • FadeOutWidget
  • BlurWidget
  • BlurInWidget
  • BlurOutWidget
  • AnimatedBlurWidget
  • WavesWidget
7. Helper class with static methods for the SharedPreferences package
8. Various widgets
  • ScrollingText
  • HorizontalSlider
  • VerticalSlider

Dependencies #

Examples built with this library: #

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

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

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) {
    final 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: {});
  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.

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>(
  streamed: 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>(
//    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 MemoryValue 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 StreamedList. 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);

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.

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>(
                streamed: 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>(
                    streamed: 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],
 ),

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);
  },
)
41
likes
0
pub points
62%
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, meta, rxdart, shared_preferences

More

Packages that depend on frideos