Creating a custom clock widget in Flutter - dart

My goal is to create a clock similar to this. How can I achieve it using Flutter?

I would recommend the Layouts, Interactivity, and Animation tutorials. The codelab is also a good way to learn your way around Flutter.
Here's a sketch of how to build your app.
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
theme: new ThemeData(
canvasColor: Colors.deepPurple,
iconTheme: new IconThemeData(color: Colors.white),
accentColor: Colors.pinkAccent,
brightness: Brightness.dark,
),
home: new MyHomePage(),
));
}
class ProgressPainter extends CustomPainter {
ProgressPainter({
#required this.animation,
#required this.backgroundColor,
#required this.color,
}) : super(repaint: animation);
/// Animation representing what we are painting
final Animation<double> animation;
/// The color in the background of the circle
final Color backgroundColor;
/// The foreground color used to indicate progress
final Color color;
#override
void paint(Canvas canvas, Size size) {
Paint paint = new Paint()
..color = backgroundColor
..strokeWidth = 5.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
paint.color = color;
double progressRadians = (1.0 - animation.value) * 2 * math.pi;
canvas.drawArc(
Offset.zero & size, math.pi * 1.5, -progressRadians, false, paint);
}
#override
bool shouldRepaint(ProgressPainter other) {
return animation.value != other.animation.value ||
color != other.color ||
backgroundColor != other.backgroundColor;
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
List<IconData> icons = <IconData>[
Icons.alarm, Icons.access_time, Icons.hourglass_empty, Icons.timer,
];
AnimationController _controller;
String get timeRemaining {
Duration duration = _controller.duration * _controller.value;
return '${duration.inMinutes} ${(duration.inSeconds % 60)
.toString()
.padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 12),
)
..reverse(from: 0.4);
}
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return new Scaffold(
body: new Padding(
padding: const EdgeInsets.all(10.0),
child:
new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: icons.map((IconData iconData) {
return new Container(
margin: new EdgeInsets.all(10.0),
child: new IconButton(
icon: new Icon(iconData), onPressed: () {
// TODO: Implement
}),
);
}).toList(),
),
new Expanded(
child: new Align(
alignment: FractionalOffset.center,
child: new AspectRatio(
aspectRatio: 1.0,
child: new Stack(
children: <Widget>[
new Positioned.fill(
child: new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new CustomPaint(
painter: new ProgressPainter(
animation: _controller,
color: themeData.indicatorColor,
backgroundColor: Colors.white,
),
);
}
),
),
new Align(
alignment: FractionalOffset.center,
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Text(
'Label', style: themeData.textTheme.subhead),
new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new Text(
timeRemaining,
style: themeData.textTheme.display4,
);
}
),
new Text('+1', style: themeData.textTheme.title),
],
),
),
],
),
),
),
),
new Container(
margin: new EdgeInsets.all(10.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new IconButton(icon: new Icon(Icons.delete), onPressed: () {
// TODO: Implement delete
}),
new FloatingActionButton(
child: new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new Icon(
_controller.isAnimating
? Icons.pause
: Icons.play_arrow
);
},
),
onPressed: () {
if (_controller.isAnimating)
_controller.stop();
else {
_controller.reverse(
from: _controller.value == 0.0 ? 1.0 : _controller
.value,
);
}
},
),
new IconButton(
icon: new Icon(Icons.alarm_add), onPressed: () {
// TODO: Implement add time
}),
],
),
),
],
),
),
);
}
}

Related

Sliding animation to bottom in flutter

I have a dot indicator in bottom of the page. I need to hide this after 5 seconds sliding to bottom. When user move to other page show dots sliding to top and finally after 5 seconds hide again. Now the dots hide after 5 seconds in fade out but i need other type of animation.
import 'package:flutter/material.dart';
import 'package:iGota/screens/partials/dots_indicator.dart';
import 'package:iGota/screens/posts_page.dart';
import 'package:iGota/screens/maps_page.dart';
class HomePage extends StatefulWidget {
static String tag = 'home-page';
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<HomePage> {
final _controller = new PageController();
static const _kDuration = const Duration(milliseconds: 300);
static const _kCurve = Curves.ease;
final _kArrowColor = Colors.black.withOpacity(0.8);
bool _visible = true;
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: 5)).then((_) => _visible = !_visible);
}
#override
Widget build(BuildContext context) {
final List<Widget> _pages = <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
child: Column(
children: <Widget>[
IconButton(
icon:
Icon(Icons.save_alt, color: Colors.white, size: 30.0),
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
),
Text(
"Contenedores",
style: TextStyle(color: Colors.white, fontSize: 20.0),
)
],
),
),
],
),
splashColor: Colors.white,
color: Colors.blue[300],
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
),
),
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
child: Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.bubble_chart,
color: Colors.white, size: 30.0),
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
),
Text(
"Válvulas",
style: TextStyle(color: Colors.white, fontSize: 20.0),
)
],
),
),
],
),
splashColor: Colors.white,
color: Colors.red[300],
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
),
),
];
return new Scaffold(
body: new IconTheme(
data: new IconThemeData(color: _kArrowColor),
child: new Stack(
children: <Widget>[
new PageView.builder(
physics: new AlwaysScrollableScrollPhysics(),
controller: _controller,
itemCount: _pages.length,
itemBuilder: (BuildContext context, int index) {
this._visible=true;
return _pages[index % _pages.length];
},
),
new Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 3000),
child: new Container(
color: Colors.grey[800].withOpacity(0.5),
padding: const EdgeInsets.all(20.0),
child: new Center(
child: new DotsIndicator(
controller: _controller,
itemCount: _pages.length,
onPageSelected: (int page) {
_controller.animateToPage(
page,
duration: _kDuration,
curve: _kCurve,
);
},
),
),
),
),
)
],
),
),
);
}
}
I think position transition would help me but i don't know exactly how can i add to my code without rewriting. So anybody can help me?
UPDATE
import 'package:flutter/material.dart';
import 'package:iGota/screens/partials/dots_indicator.dart';
import 'package:iGota/screens/posts_page.dart';
import 'package:iGota/screens/maps_page.dart';
class HomePage extends StatefulWidget {
static String tag = 'home-page';
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
final _controller = new PageController();
static const _kDuration = const Duration(milliseconds: 300);
static const _kCurve = Curves.ease;
final _kArrowColor = Colors.black.withOpacity(0.8);
AnimationController controller;
Animation<Offset> offset;
#override
void initState() {
super.initState();
controller =AnimationController(vsync: this, duration: Duration(seconds: 1));
Future.delayed(Duration(seconds: 5)).then((_) => controller.forward());
offset = Tween<Offset>(begin: Offset.zero, end: Offset(0.0, 1.0))
.animate(controller);
}
#override
Widget build(BuildContext context) {
GestureDetector(onTap: () {
setState(() {
controller.reverse();
});
});
final List<Widget> _pages = <Widget>[
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
child: Column(
children: <Widget>[
IconButton(
icon:
Icon(Icons.save_alt, color: Colors.white, size: 30.0),
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
),
Text(
"Contenedores",
style: TextStyle(color: Colors.white, fontSize: 20.0),
)
],
),
),
],
),
splashColor: Colors.white,
color: Colors.blue[300],
onPressed: () {
Navigator.pushNamed(context, PostsPage.tag);
},
),
),
new ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: new FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
child: Column(
children: <Widget>[
IconButton(
icon: Icon(Icons.bubble_chart,
color: Colors.white, size: 30.0),
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
),
Text(
"Válvulas",
style: TextStyle(color: Colors.white, fontSize: 20.0),
)
],
),
),
],
),
splashColor: Colors.white,
color: Colors.red[300],
onPressed: () {
Navigator.pushNamed(context, MapsPage.tag);
},
),
),
];
return new Scaffold(
body: new IconTheme(
data: new IconThemeData(color: _kArrowColor),
child: new Stack(
children: <Widget>[
new PageView.builder(
physics: new AlwaysScrollableScrollPhysics(),
controller: _controller,
itemCount: _pages.length,
itemBuilder: (BuildContext context, int index) {
return _pages[index % _pages.length];
},
),
new Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: SlideTransition(
position: offset,
child: new Container(
color: Colors.grey[800].withOpacity(0.5),
padding: const EdgeInsets.all(20.0),
child: new Center(
child: new DotsIndicator(
controller: _controller,
itemCount: _pages.length,
onPageSelected: (int page) {
_controller.animateToPage(
page,
duration: _kDuration,
curve: _kCurve,
);
},
),
),
),
),
)
],
),
),
);
}
}
Now i need to call controller.reverse when user touch screen...
To create a sliding animation for your indicator (if I've understood your requirement right), I would simply suggest using the SlideTransition widget. It should not require much work to integrate it in your existing code.
The code belows shows a minimal example of the SlideTransition. If you'd like to keep displaying it during the navigation from one screen to another, you'd have to draw it in a layer above your Navigator.
If you do not like to use a Stack, you can instead use the Overlay functionality of flutter, as given in this answer. This would also solve the struggle, with keeping the animation displayed during the navigation transition.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() => HomeState();
}
class HomeState extends State<Home> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<Offset> offset;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(seconds: 1));
offset = Tween<Offset>(begin: Offset.zero, end: Offset(0.0, 1.0))
.animate(controller);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Center(
child: RaisedButton(
child: Text('Show / Hide'),
onPressed: () {
switch (controller.status) {
case AnimationStatus.completed:
controller.reverse();
break;
case AnimationStatus.dismissed:
controller.forward();
break;
default:
}
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: SlideTransition(
position: offset,
child: Padding(
padding: EdgeInsets.all(50.0),
child: CircularProgressIndicator(),
),
),
)
],
),
);
}
}

Destruct and Construct cards in Flutter dynamically

I'm new to Flutter,
I want to destruct cards created initially and construct them again as per data provided in API call.
Basically when I tap on button in UI, it should call APIs and based on data from API call, if it is different from the data I already have, I want to destruct cards and construct them again.
How I can achieve this?
The cards will auto update their content when you make the call again, it is like refreshing your data.
I have made a simple example with a single card that shows data from this JSON Where I am calling the API first time in initState and then repeating the call each time I press on the FAB.
I am adding the index variable just to show you the updates (updating my single card with the next item in the list)
Also it is worth noting that I am handling the null or empty values poorly for the sake of time.
Also forget about the UI overflow ¯_(ツ)_/¯
class CardListExample extends StatefulWidget {
#override
_CardListExampleState createState() => new _CardListExampleState();
}
class _CardListExampleState extends State<CardListExample> {
Map cardList = {};
int index = 0;
#override
void initState() {
_getRequests();
super.initState();
}
_getRequests() async {
String url = "https://jsonplaceholder.typicode.com/users";
var httpClinet = createHttpClient();
var response = await httpClinet.get(
url,
);
var data = JSON.decode(response.body);
//print (data);
setState(() {
this.cardList = data[index];
this.index++;
});
print(cardList);
print(cardList["name"]);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton:
new FloatingActionButton(onPressed: () => _getRequests()),
appBar: new AppBar(
title: new Text("Card List Example"),
),
body: this.cardList != {}
? new ListView(children: <Widget>[
new Card(
child: new Column(
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Text(
cardList["name"] ?? '',
style: Theme.of(context).textTheme.display1,
),
new Text(
this.cardList['email'] ?? '',
maxLines: 50,
),
],
),
new Text(cardList["website"] ?? '')
],
),
),
])
: new Center(child: new CircularProgressIndicator()),
);
}
}
Yes, Answer from Aziza works.
Though I used the code as below :
void main() =>
runApp(new MaterialApp(
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/about':
return new FromRightToLeft(
builder: (_) => new _aboutPage.About(),
settings: settings,
);
}
},
home : new HomePage(),
theme: new ThemeData(
fontFamily: 'Poppins',
primarySwatch: Colors.blue,
),
));
class HomePage extends StatefulWidget{
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage>{
List data;
Future<String> getData() async{
var response = await http.get(
Uri.encodeFull(<SOMEURL>),
headers: {
"Accept" : "application/json"
}
);
this.setState((){
data = JSON.decode(response.body);
});
return "Success";
}
#override
void initState() {
// TODO: implement initState
super.initState();
this.getData();
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar : new AppBar(
title : new Text("ABC API"),
actions: <Widget>[
new IconButton( // action button
icon: new Icon(Icons.cached),
onPressed: () => getData(),
)],
),
drawer: new Drawer(
child: new ListView(
children: <Widget> [
new Container(
height: 120.0,
child: new DrawerHeader(
padding: new EdgeInsets.all(0.0),
decoration: new BoxDecoration(
color: new Color(0xFFECEFF1),
),
child: new Center(
child: new FlutterLogo(
colors: Colors.blueGrey,
size: 54.0,
),
),
),
),
new ListTile(
leading: new Icon(Icons.chat),
title: new Text('Support'),
onTap: () {
Navigator.pop(context);
Navigator.of(context).pushNamed('/support');
}
),
new ListTile(
leading: new Icon(Icons.info),
title: new Text('About'),
onTap: () {
Navigator.pop(context);
Navigator.of(context).pushNamed('/about');
}
),
new Divider(),
new ListTile(
leading: new Icon(Icons.exit_to_app),
title: new Text('Sign Out'),
onTap: () {
Navigator.pop(context);
}
),
],
)
),
body: this.data != null ?
new ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index){
return new Container(
padding: new EdgeInsets.fromLTRB(8.0,5.0,8.0,0.0),
child: new Card(
child: new Padding(
padding: new EdgeInsets.fromLTRB(10.0,12.0,8.0,0.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ListTile(
enabled: data[index]['active'] == '1' ? true : false,
title: new Text(data[index]['header'],
style:Theme.of(context).textTheme.headline,
),
subtitle: new Text("\n" + data[index]['description']),
),
new ButtonTheme.bar(
child: new ButtonBar(
children: <Widget>[
new FlatButton(
child: new Text(data[index]['action1']),
onPressed: data[index]['active'] == '1' ? _launchURL :null,
),
],
),
),
],
),
),
),
);
},
)
:new Center(child: new CircularProgressIndicator()),
);
}
}
_launchURL() async {
const url = 'http://archive.org';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
class FromRightToLeft<T> extends MaterialPageRoute<T> {
FromRightToLeft({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
return new SlideTransition(
child: new Container(
decoration: new BoxDecoration(
boxShadow: [
new BoxShadow(
color: Colors.black26,
blurRadius: 25.0,
)
]
),
child: child,
),
position: new Tween(
begin: const Offset(1.0, 0.0),
end: const Offset(0.0, 0.0),
)
.animate(
new CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
)
),
);
}
#override Duration get transitionDuration => const Duration(milliseconds: 400);
}
The above code includes Navigation drawer, page navigation animation and also answer to the above question.

My activity is lag, it it because of the code?

class _DaftarMuridState extends State<DaftarMurid> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Column(
children: <Widget>[
new Flexible(
child: new FirebaseAnimatedList(//new
query: db.reference().child("Murid"),
sort: (a, b) => a.key.compareTo(b.key),
padding: new EdgeInsets.all(8.0),
itemBuilder: (_, DataSnapshot dataSnapshot, Animation<double> animations,x){
return new DaftarMuridView(
snapshot: dataSnapshot,
animation: animations,
);//new
}
),
),
],
),
);
}
}
class DaftarMuridViewState extends State<DaftarMuridView>{
DaftarMuridViewState({this.snapshot, this.animation});
final DataSnapshot snapshot;
final Animation animation;
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
String fotoUrl = snapshot.value['Foto'];
String ig = snapshot.value['Instagram'];
hash.putIfAbsent(snapshot.value['Nama'], () => false);
bool expanded = hash[snapshot.value['Nama']];
var expansionPanel = new ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
hash.remove(snapshot.value['Nama']);
hash.putIfAbsent(snapshot.value['Nama'], () => !isExpanded);
expanded = !expanded;
});
},
children: [new ExpansionPanel(headerBuilder: (BuildContext context, bool isExpanded) {
return new ListTile(
leading: const Icon(Icons.school),
title: new Text(
snapshot.value['Nama'],
textAlign: TextAlign.left,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
));
},
body: new ListView(
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
children: <Widget>[
new CachedNetworkImage(
imageUrl: fotoUrl == null?"https://drive.google.com/uc?export=download&id=1tkqO59S9jiWpkzHQNJRKLuCGYIn5kK_v":fotoUrl,
placeholder: new CircularProgressIndicator(),
errorWidget: new CachedNetworkImage(imageUrl: "https://drive.google.com/uc?export=download&id=1tkqO59S9jiWpkzHQNJRKLuCGYIn5kK_v"),
fadeOutDuration: new Duration(seconds: 1),
fadeInDuration: new Duration(seconds: 1),
height: size.height / 2.0,
width: size.width / 2.0,
alignment: Alignment.center,
),
new ListTile(
leading: const Icon(Icons.today),
title: const Text('Tanggal Lahir'),
subtitle: new Text(snapshot.value['Tanggal Lahir']),
),
new Row(
children: <Widget>[
ig != null ?
new FlatButton(
onPressed: () => _instagram(ig),
child: new CachedNetworkImage(imageUrl: "http://diylogodesigns.com/blog/wp-content/uploads/2016/05/Instagram-logo-png-icon.png", width: size.width / 4.0, height: size.height / 4.0, ),
)
: new Container(),
],
),
],
),
isExpanded: expanded)],
);
return new SizeTransition(
sizeFactor: new CurvedAnimation(
parent: animation, curve: Curves.easeOut),
axisAlignment: 0.0,
child: expansionPanel,
);
}
}
is my code not efficient? the process is Get Data from Firebase -> Store it to list view
it's a bit lag when open the activity, maybe because getting the data. But is there a solution for make it doesn't lag?
I cut some code that isn't important.
Use Futures to perform time consuming operations, so it will not freeze the UI.
https://www.dartlang.org/tutorials/language/futures

Flutter floating action button with speed dial

Is there any ready made widget or where to get started floating action button with speed dial actions in Flutter.
Here's a sketch of how to implement a Speed dial using FloatingActionButton.
import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
State createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController _controller;
static const List<IconData> icons = const [ Icons.sms, Icons.mail, Icons.phone ];
#override
void initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
}
Widget build(BuildContext context) {
Color backgroundColor = Theme.of(context).cardColor;
Color foregroundColor = Theme.of(context).accentColor;
return new Scaffold(
appBar: new AppBar(title: new Text('Speed Dial Example')),
floatingActionButton: new Column(
mainAxisSize: MainAxisSize.min,
children: new List.generate(icons.length, (int index) {
Widget child = new Container(
height: 70.0,
width: 56.0,
alignment: FractionalOffset.topCenter,
child: new ScaleTransition(
scale: new CurvedAnimation(
parent: _controller,
curve: new Interval(
0.0,
1.0 - index / icons.length / 2.0,
curve: Curves.easeOut
),
),
child: new FloatingActionButton(
heroTag: null,
backgroundColor: backgroundColor,
mini: true,
child: new Icon(icons[index], color: foregroundColor),
onPressed: () {},
),
),
);
return child;
}).toList()..add(
new FloatingActionButton(
heroTag: null,
child: new AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return new Transform(
transform: new Matrix4.rotationZ(_controller.value * 0.5 * math.pi),
alignment: FractionalOffset.center,
child: new Icon(_controller.isDismissed ? Icons.share : Icons.close),
);
},
),
onPressed: () {
if (_controller.isDismissed) {
_controller.forward();
} else {
_controller.reverse();
}
},
),
),
),
);
}
}
This plugin could serve you:
https://pub.dartlang.org/packages/flutter_speed_dial
You need declare the dependency in the pubspect.yaml file
dependencies:
flutter:
sdk: flutter
flutter_speed_dial: ^1.0.9
Here is an example:
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
animatedIconTheme: IconThemeData(size: 22.0),
// this is ignored if animatedIcon is non null
// child: Icon(Icons.add),
visible: _dialVisible,
curve: Curves.bounceIn,
overlayColor: Colors.black,
overlayOpacity: 0.5,
onOpen: () => print('OPENING DIAL'),
onClose: () => print('DIAL CLOSED'),
tooltip: 'Speed Dial',
heroTag: 'speed-dial-hero-tag',
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 8.0,
shape: CircleBorder(),
children: [
SpeedDialChild(
child: Icon(Icons.accessibility),
backgroundColor: Colors.red,
label: 'First',
labelStyle: TextTheme(fontSize: 18.0),
onTap: () => print('FIRST CHILD')
),
SpeedDialChild(
child: Icon(Icons.brush),
backgroundColor: Colors.blue,
label: 'Second',
labelStyle: TextTheme(fontSize: 18.0),
onTap: () => print('SECOND CHILD'),
),
SpeedDialChild(
child: Icon(Icons.keyboard_voice),
backgroundColor: Colors.green,
label: 'Third',
labelStyle: TextTheme(fontSize: 18.0),
onTap: () => print('THIRD CHILD'),
),
],
),
);
}

Overlay a line on top of a component in Flutter

I have the following layout in one of my components and would like to put a line on top of that like this:
That is my current code and already searched through the API documentation of Flutter for a while now and didn't find something suitable to achieve that.
new Row(
children: <Widget>[
new Expanded(
child: const Text("Some text"),
),
const Text("Some other text"),
],
)
Any pointers or ideas how to do that?
Okay. Got it working by using a custom Decoration.
Here is my code:
class StrikeThroughDecoration extends Decoration {
#override
BoxPainter createBoxPainter([VoidCallback onChanged]) {
return new _StrikeThroughPainter();
}
}
class _StrikeThroughPainter extends BoxPainter {
#override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final paint = new Paint()
..strokeWidth = 1.0
..color = Colors.black
..style = PaintingStyle.fill;
final rect = offset & configuration.size;
canvas.drawLine(new Offset(rect.left, rect.top + rect.height / 2), new Offset(rect.right, rect.top + rect.height / 2), paint);
canvas.restore();
}
}
Used like that in my component:
new Container(
foregroundDecoration: new StrikeThroughDecoration(),
child: new Row(
children: <Widget>[
new Expanded(
child: const Text("Some text"),
),
const Text("Some other text"),
],
)
)
You could use a Stack with a Divider.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Example App')),
body: new Padding(
padding: new EdgeInsets.all(5.0),
child: new Center(
child: new Stack(
children: <Widget>[
new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text('Hello world'),
new Text('Some other text'),
],
),
new Positioned.fill(
left: 0.0,
right: 0.0,
child: new Divider(color: Colors.black),
)
],
),
),
),
);
}
}

Resources