frideos 0.4.1 frideos: ^0.4.1 copied to clipboard
Helpers for state management, streams and BLoC pattern, SharedPreferences and various widgets (animations, blur, transitions, timed widgets etc.).
Frideos #
Helpers for state management, streams and BLoC pattern, SharedPreferences and various widgets (animations, blur, transitions, timed widgets, scrollingtext, etc.).
- Helpers for state management
- AppStateModel
- AppStateProvider
- Helpers for streams and BLoC pattern:
- StreamedValue
- StreamedTransformed
- StreamedList
- StreamedMap
- MemoryValue
- HistoryObject
- StreamedSender
- Tunnel pattern: ListSender and 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.');
}
});
}
-
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
addText() {
var key = int.parse(_key.value);
var value = _text.value;
var streamMap = _map.value;
if (streamMap != null) {
map.addAll(streamMap);
}
map[key] = value;
inMap(map);
}
dispose() {
print('-------StreamedMapClean BLOC DISPOSE--------');
_map.close();
_text.close();
_key.close();
}
}
-
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
streamedKey.debugMode();
streamedKey.debugMode();
streamedMap.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
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();
}
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 share data between two blocs.
StreamedSender #
Used to make a one-way tunnel beetween two blocs (from blocA to a StremedValue on blocB).
Usage
-
Define an object that implements the [StreamedObject] interface in the blocB (e.g. a StreamedValue):
final receiverStr = StreamedValue<String>();
-
Define a [StreamedSender] in the blocA:
final tunnelSenderStr = StreamedSender<String>();
-
Set the receiver in the sender on the class the holds the instances of the blocs:
blocA.tunnelSenderStr.setReceiver(blocB.receiverStr);
-
To send data from blocA to bloc B then:
tunnelSenderStr.send("Text from blocA to blocB");
ListSender and MapSender #
Like the StreamedSender, but used with collections.
Usage
-
Define a [StreamedList] or [StreamedMap] object in the blocB
final receiverList = StreamedList<int>();
final receiverMap = StreamedMap<int, String>();
-
Define a [ListSender]/[MapSender] in the blocA
final tunnelList = ListSender<int>();
final tunnelMap = MapSender<int, String>();
-
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);
-
To send data from blocA to bloc B 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:
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.
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:
-
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: () {}),
}
-
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();
}
}
-
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 #
Usage
-
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: () {}),
}
-
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].
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.
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 #
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 #
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).