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'),
),
],
),
);
}
Related
My build was working fine until I added an image to the launch screen, when I started getting a PhaseScript error. I had to completely delete the ios folder, recreate it, and then make the necessary manual changes again. Now when I run the build on a simulator through android studio, I get this error once the app starts:
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: Bad state: No element
#0 List.first (dart:core-patch/growable_array.dart:339:5)
#1 main (package:globe_flutter/main.dart:19:25)
<asynchronous suspension>
Nothing shows up on the screen. I use a tabBar in the main.dart and a tabBarView to show the different screens. Here is the main.dart:
var firstCamera;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
firstCamera = cameras.first;
await Firebase.initializeApp();
runApp(MyApp());
}
// void main() async {
// WidgetsFlutterBinding.ensureInitialized();
// await Firebase.initializeApp();
// runApp(MyApp());
// }
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'GLOBE',
debugShowCheckedModeBanner: false,
home: const MyHome(),
routes: {
LoginScreen.id: (context) => LoginScreen(),
SignupScreen.id: (context) => SignupScreen(),
HomeScreen.id: (context) => HomeScreen()
},
);
}
}
class MyHome extends StatefulWidget {
const MyHome({Key? key}) : super(key: key);
#override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> with SingleTickerProviderStateMixin {
late TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(length: 5, vsync: this);
}
#override
void dispose() {
super.dispose();
_tabController.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: TabBarView(
children: [
HomeScreen(),
HomeScreen(),
TakePictureScreen(camera: firstCamera),
HomeScreen(),
ProfileScreen(username: "cjmofficial")
],
controller: _tabController
),
extendBody: true,
bottomNavigationBar: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 40, horizontal: 40),
child: ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(50.0),
child: Container(
height: 55,
color: Colors.grey[200],
child: TabBar(
labelColor: Colors.teal[200],
unselectedLabelColor: Colors.blueGrey,
indicator: UnderlineTabIndicator(
borderSide: BorderSide(color: Colors.teal),
insets: EdgeInsets.fromLTRB(50, 0, 50, 40)
),
indicatorColor: Colors.teal,
tabs: [
Tab(icon: Icon(Icons.home_outlined)),
Tab(icon: Icon(Icons.explore_outlined)),
Tab(icon: Icon(Icons.camera_alt_outlined)),
Tab(icon: Icon(Icons.movie_outlined)),
Tab(icon: Icon(Icons.person_outline))
],
controller: _tabController),
),
),
),
);
}
}
and the first screen that is supposed to show in the TabBarView:
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
static const String id = "home_screen";
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Color _likeButtonColor = Colors.black;
Widget _buildPost(String username, String imageUrl, String caption, String pfpUrl) {
return Container(
color: Colors.white,
child: Column(
children: [
GestureDetector(
onTap: (){},
child: Container(
height: 50,
color: Colors.deepOrangeAccent[100],
child: Row(
children: [
SizedBox(width: 5),
CircleAvatar(
child: ClipOval(
child: Image.network(
pfpUrl,
height: 40,
width: 40,
),
),
),
SizedBox(width: 10),
Icon(Icons.more_vert, size: 20),
SizedBox(width: 10),
Text(username, style: TextStyle(fontSize: 15))
],
),
),
),
Stack(
children: [
Image.asset("images/post_background.jpg"),
Padding(
padding: const EdgeInsets.all(20.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.network(imageUrl, fit: BoxFit.cover)),
),
],
),
Container(
height: 100,
child: Column(
children: [
const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
setState(() {
HapticFeedback.lightImpact();
});
},
icon: Icon(Icons.thumb_up_alt_outlined, size: 30)),
Text("l", style: TextStyle(fontSize: 30)),
IconButton(
onPressed: () {
setState(() {
HapticFeedback.lightImpact();
GallerySaver.saveImage(imageUrl);
});
},
icon: Icon(Icons.ios_share, size: 30))
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(caption, style: const TextStyle(fontSize: 15))
],
)
],
),
)
],
),
);
}
List<Post> listPosts = [];
fetchPosts() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
result.docs.forEach((res) async {
print(res.id);
QuerySnapshot posts = await userRef.doc(res.id).collection("posts").get();
posts.docs.forEach((res) {
listPosts.add(Post.fromJson(res.data() as Map<String, dynamic>));
});
// Other method
// listPosts = posts.docs.map((doc) => Post.fromJson(doc.data() as Map<String, dynamic>)).toList();
setState(() {});
});
setState(() {
listPosts.sort((a, b) => a.postedDate.compareTo(b.postedDate));
});
}
pfpUrl(String username) async {
// Get pfpUrl
String downloadURL = await FirebaseStorage.instance
.ref(username + "/profile_picture.png")
.getDownloadURL();
return downloadURL;
}
#override
void initState() {
fetchPosts();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: AppBar(
leading:
Icon(Icons.monetization_on_outlined, color: Colors.blueGrey),
centerTitle: true,
title: Text("GLOBE", style: TextStyle(color: Colors.blueGrey)),
actions: [
Icon(Icons.search, color: Colors.blueGrey),
SizedBox(width: 10)
],
backgroundColor: Colors.tealAccent[100]),
),
body: ListView.builder(
itemCount: listPosts.length,
itemBuilder: (BuildContext context, int index) {
// We retrieve the post at index « index »
final post = listPosts[index];
// Get name from id
var parts = post.id.split('_');
var username = parts[0].trim();
// Get pfpUrl
String pfpUrlString = "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3";
return _buildPost(username, post.postUrlString, post.caption, pfpUrlString);
}),
);
}
}
I'm new to flutter and all I can understand about this is that something cannot load. I also ran a build with verbose but it didn't give anymore information. If someone can help me understand the issue, that would be great.
Ios simulators do not have cameras so on line 19, when firstCamera = cameras.first is called, there is no first camera.
I'm trying to animate two square containers so that when they are tapped they are animated to scale. I see all these transform class examples online that show animation of a widget however when I use the transform class the scale just jumps from its initial value to its final value.
My end goal is to animate a container to 'bounce' every time it is tapped like what you can do with bounce.js in web development. To understand what I mean you can go to http://bouncejs.com, click 'select preset' in the upper left corner, select jelly from the drop down menu and click 'play animation'.
Can this be done with the transform class?
Here is my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
var squareScaleA = 0.5;
var squareScaleB = 0.5;
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bounce Example"),
),
body: Stack(
children: <Widget>[
Container(
width: 300.0,
height: 150.0,
color: Colors.yellowAccent,
),
Column(
children: <Widget>[
Row(
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
squareScaleA = 1.0;
});
},
child: Transform.scale(
scale: squareScaleA,
child: Container(
width: 150.0,
height: 150.0,
color: Colors.green,
),
),
),
GestureDetector(
onTap: () {
setState(() {
squareScaleB = 1.0;
});
},
child: Transform.scale(
scale: squareScaleB,
child: Container(
width: 150.0,
height: 150.0,
color: Colors.blue,
),
),
),
],
),
],
),
],
),
);
}
}
Thanks in advance for any help!
You need to use Animations, you can start using AnimationController it's very simple , I fixed your sample :
class _MyHomePageState extends State<TestingNewWidget>
with TickerProviderStateMixin {
var squareScaleA = 0.5;
var squareScaleB = 0.5;
AnimationController _controllerA;
AnimationController _controllerB;
#override
void initState() {
_controllerA = AnimationController(
vsync: this,
lowerBound: 0.5,
upperBound: 1.0,
duration: Duration(seconds: 1));
_controllerA.addListener(() {
setState(() {
squareScaleA = _controllerA.value;
});
});
_controllerB = AnimationController(
vsync: this,
lowerBound: 0.5,
upperBound: 1.0,
duration: Duration(seconds: 1));
_controllerB.addListener(() {
setState(() {
squareScaleB = _controllerB.value;
});
});
super.initState();
}
#override
void dispose() {
_controllerA.dispose();
_controllerB.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Bounce Example"),
),
body: Stack(
children: <Widget>[
Container(
width: 300.0,
height: 150.0,
color: Colors.yellowAccent,
),
Column(
children: <Widget>[
Row(
children: <Widget>[
GestureDetector(
onTap: () {
if (_controllerA.isCompleted) {
_controllerA.reverse();
} else {
_controllerA.forward(from: 0.0);
}
},
child: Transform.scale(
scale: squareScaleA,
child: Container(
width: 150.0,
height: 150.0,
color: Colors.green,
),
),
),
GestureDetector(
onTap: () {
if (_controllerB.isCompleted) {
_controllerB.reverse();
} else {
_controllerB.forward(from: 0.0);
}
},
child: Transform.scale(
scale: squareScaleB,
child: Container(
width: 150.0,
height: 150.0,
color: Colors.blue,
),
),
),
],
),
],
),
],
),
);
}
}
Also you can read more about animation here: https://flutter.dev/docs/development/ui/animations
For my flutter app I need a container that can be added when I press the add button. So I then looked at other Stack Overflow questions such as: Flutter - Render new Widgets on click and Flutter - Add new Widget on click. After, this is what I came up with.
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
var tPadding= MediaQuery.of(context).size.width * 0.08;
var bPadding= MediaQuery.of(context).size.width * 0.15;
var vPadding = MediaQuery.of(context).size.height * 0.015;
return Expanded (
child: Container (
child: NotificationListener<OverscrollIndicatorNotification> (
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: container.length,
itemBuilder: (context, i) {
return Padding (
padding: EdgeInsets.only(
left: vPadding,
right: vPadding,
top: tPadding,
bottom: bPadding
),
child: container[i]
);
},
)
)
)
);
}
}
int _count = 1;
List container = [
List.generate(_count, (int i) => ContainerCard),
AddContainer()
];
class ContainerCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Material (
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
);
}
}
class AddContainer extends StatefulWidget {
#override
AddContainerState createState() => AddContainerState();
}
class AddContainerState extends State<AddContainer> {
#override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell (
onTap: _addContainer,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon (
Icons.add,
size: 50.0,
)
]
),
)
);
}
void _addContainer() {
setState(() {
_count = _count + 1;
});
}
}
But for some reason this is not working. What is wrong and how can I fix this?
Full Code:
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp (
debugShowCheckedModeBanner: false,
home: Scaffold (
backgroundColor: Colors.white,
body: Column (
children: <Widget> [
AppBar(),
Body(),
]
)
)
);
}
}
class AppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
var abHeight = MediaQuery.of(context).size.height * 0.2;
var vPadding = MediaQuery.of(context).size.height * 0.07;
return Container (
height: abHeight,
child: Column (
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget> [
Row (
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: vPadding),
child: PoppinsText (
text: "App",
fontSize: 40.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
)
]
)
]
)
);
}
}
class Body extends StatelessWidget {
#override
Widget build(BuildContext context) {
var tPadding= MediaQuery.of(context).size.width * 0.08;
var bPadding= MediaQuery.of(context).size.width * 0.15;
var vPadding = MediaQuery.of(context).size.height * 0.015;
return Expanded (
child: Container (
child: NotificationListener<OverscrollIndicatorNotification> (
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: container.length,
itemBuilder: (context, i) {
return Padding (
padding: EdgeInsets.only(
left: vPadding,
right: vPadding,
top: tPadding,
bottom: bPadding
),
child: container[i]
);
},
)
)
)
);
}
}
int _count = 1;
List container = [
List.generate(_count, (int i) => ContainerCard),
AddContainer()
];
class ContainerCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Material (
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
);
}
}
class AddContainer extends StatefulWidget {
#override
AddContainerState createState() => AddContainerState();
}
class AddContainerState extends State<AddContainer> {
#override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell (
onTap: _addContainer,
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column (
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon (
Icons.add,
size: 50.0,
)
]
),
)
);
}
void _addContainer() {
setState(() {
_count = _count + 1;
});
}
}
class PoppinsText extends StatelessWidget {
PoppinsText ({Key key,
this.text,
this.fontSize,
this.fontWeight,
this.color}) : super(key: key);
final String text;
final double fontSize;
final FontWeight fontWeight;
final Color color;
#override
Widget build(BuildContext context) {
return Text (
text,
style: TextStyle (
fontFamily: 'Poppins',
fontWeight: fontWeight,
fontSize: fontSize,
color: color
),
);
}
}
You are using a Stateless widget. Switch to Stateful widget
Edit
Updated as per requirement.
The trick here is to use reverse property of pageview.
class Body extends StatefulWidget {
#override
_BodyState createState() => _BodyState();
}
class _BodyState extends State<Body> {
int count = 1;
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
child: NotificationListener<OverscrollIndicatorNotification>(
onNotification: (OverscrollIndicatorNotification overscroll) {
overscroll.disallowGlow();
},
child: PageView.builder(
reverse: true,
pageSnapping: false,
controller: PageController(viewportFraction: 0.85),
itemCount: count,
itemBuilder: (context, i) {
print(i);
if (i == 0) {
return Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height * 0.015,
right:
MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom:
MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
child: InkWell(
onTap: () {
setState(() {
count++;
});
},
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.add,
size: 50.0,
)
]),
)));
} else {
return Padding(
padding: EdgeInsets.only(
left:
MediaQuery.of(context).size.height * 0.015,
right:
MediaQuery.of(context).size.height * 0.015,
top: MediaQuery.of(context).size.width * 0.08,
bottom:
MediaQuery.of(context).size.width * 0.15),
child: Material(
borderRadius: BorderRadius.circular(50.0),
color: Colors.white,
elevation: 8.0,
));
}
}))));
}
I don't know If you found your solution yet. This is my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
const double paddingInset = 5;
Color tileColor = Colors.grey[350];
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Test App',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
List<Widget> bodyElements = [];
int num = 0;
void addBodyElement() {
bodyElements.add(
Padding(
padding: const EdgeInsets.all(paddingInset),
child: Container(
height: 500,
width: double.infinity,
child: Center(child: Text('This is section $num')),
decoration: BoxDecoration(
color: tileColor,
borderRadius: BorderRadius.circular(10),
),
),
),
);
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Center(child: Text('Home')),
brightness: Brightness.dark,
leading: IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
setState(() {
bodyElements.clear();
num = 0;
});
},
),
),
body: ListView(
children: <Widget>[
Column(
children: bodyElements,
),
],
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.add),
label: Text('Add'),
onPressed: () {
num++;
setState(() {
addBodyElement();
});
},
),
);
}
}
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(),
),
),
)
],
),
);
}
}
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
}),
],
),
),
],
),
),
);
}
}