Flutter - changing a Stack Order - stack

I have a Stack where on a condition (e.g. user click), I want one of the lower order widgets to be pushed to the top of the stack. Using the code below as a simple example - what code do I need in a setState() method to reorder so that the first (bottom) widget becomes the last (top) widget?
new Stack(
children: <Widget>[
new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(200, 100, 180, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(000, 10, 130, 1.0)),
),
new Positioned(
left:40.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
)
],
);
I have edited the proposed solution and the stack does not change order. Here is the sample code in full (the print statement print to the console as expected on button press):
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
AnimationController timerController;
void main() => runApp(MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
AnimationController timerController;
#override
Widget build(BuildContext context) {
List<Widget> stackChildren = <Widget>[
new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(50, 50, 50, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(50, 100, 150, 1.0)),
),
];
void swapStackChildren() {
setState(() {
print("swapStackChildren");
stackChildren = [
new Positioned(
left: 40.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(150, 00, 200, 1.0))),
new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 100.0,
color: const Color.fromRGBO(200, 200, 100, 1.0)),
];
});
}
return Scaffold(
body: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Stack(children: stackChildren),
new RaisedButton(
child: const Text('Swop'),
color: Theme.of(context).accentColor,
elevation: 4.0,
splashColor: Colors.blueGrey,
onPressed: () {
swapStackChildren();
},
),
],
),
),
);
}
}

Make a variable in your widget that keeps track of the children:
List<Widget> stackChildren = <Widget>[
new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(200, 100, 180, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(000, 10, 130, 1.0)),
),
new Positioned(
left:40.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
)
];
Then in whatever function you have to trigger the order switch, you can just call the following function:
void swapStackChildren() {
final temp = stackChildren[0];
setState(() {
stackChildren[0] = stackChildren[2];
stackChildren[2] = temp;
});
}
Edit: As suggested by the comments, it's a better idea just to assign a new value to stackChildren instead of modifying it. So you should instead do something like this:
void swapStackChildren() {
setState(() {
stackChildren = [
new Positioned(
left: 40.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(218, 165, 32, 1.0))),
new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(200, 100, 180, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(000, 10, 130, 1.0)),
),
];
});
}
Edit:
Here is with the full sample code:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
AnimationController timerController;
void main() => runApp(MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
AnimationController timerController;
List<Widget> stackChildren = <Widget>[
new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(50, 50, 50, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(50, 100, 150, 1.0)),
),
];
void swapStackChildren() {
setState(() {
print("swapStackChildren");
stackChildren = [
new Positioned(
left: 40.0,
child: new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 60.0,
color: const Color.fromRGBO(150, 00, 200, 1.0))),
new Icon(Icons.monetization_on,
key: GlobalKey(),
size: 100.0,
color: const Color.fromRGBO(200, 200, 100, 1.0)),
];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Stack(children: stackChildren),
new RaisedButton(
child: const Text('Swop'),
color: Theme.of(context).accentColor,
elevation: 4.0,
splashColor: Colors.blueGrey,
onPressed: () {
swapStackChildren();
},
),
],
),
),
);
}
}

I've found a less code intensive solution .You can use the Visibility widget which you can control the visibility of the child widget after the change of state .
Visibility(
visible:visibility,//it takes bool
//here you can add the bottom widget that you want on top keeping it invisible
),
make a copy of your bottom widget and add it on top wrapped in the Visibility method as invisible and wrap your bottom widget as well .So when you want to switch you can make your bottom widget invisible and your top visible and switch accordingly
new Stack(
children: <Widget>[
Visibility(
visible:topvisibility
new Positioned(
left:40.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
)
),
new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(200, 100, 180, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const
Color.fromRGBO(000, 10, 130, 1.0)),
),
Visibility(
visible:bottomvisibility
new Positioned(
left:40.0,
child: new Icon(Icons.monetization_on, key: GlobalKey(), size: 60.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
)
),
],
);
Then change the visibility bool accordingly in setState() method.

You can refer to this article about reordering stack items- LINK
Also don't forget to assign keys to the stack widgets if you don't want to re-render the whole widget when setState(){} is called.

Related

Flutter app crashes very often after populating it with data and no errors are showed, just lost connection to device

I have a flutter app that is complex enough and i have been developing it for a while.
the problem is after finishing up with the design code , i started implementing API's to receive actual data and populate the app with it. the app is now crashing quite a lot ( although a little less in release mode).
I am thinking it might be because the android device runs out of memory because of all the complex widgets I display in the app.
I have build all of my widgets Stateful I don't know if that could be a related factor (I am a beginner with flutter programming).
Please I have spent a lot of time in developing this app and it's my first enterprise level flutter app.
I haven't had experience with flutter before and I knew this kind of problems would arise and have no solution I would have gone with Java and Swift to develop the app.
Any help is appreciated guys. Thanks.
the app only displays about a 30-40 images, I don't know where all the data coming from.
Here is the code :
import 'package:flutter/material.dart';
import 'package:bestiee/componenets/constants.dart';
import 'package:bestiee/componenets/cards/single-item-hot-screen-card.dart';
import 'package:bestiee/componenets/buttons/small-round-more-button.dart';
import 'package:bestiee/componenets/cards/single-place-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-person-hot-screen-card.dart';
import 'package:bestiee/componenets/cards/single-place-large-hot-screen-card.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-items-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-places-screen.dart';
import 'package:bestiee/screens/hotScreenSingleScreens/more-hot-people-screen.dart';
import 'package:bestiee/translations.dart';
import 'package:bestiee/models/models.dart';
import 'package:bestiee/networking.dart';
import 'package:bestiee/constants.dart';
import 'dart:convert';
import 'package:http/http.dart';
class HotScreen extends StatefulWidget {
static List<Item> items = [];
static List<Place> places = [];
static List<Person> people = [];
static bool isFirstRun = true;
#override
_HotScreenState createState() => _HotScreenState();
}
class _HotScreenState extends State<HotScreen> {
String getTranslation(String text) {
try {
String translation = Translations.of(context).text(text);
return translation;
} catch (ex) {
print(ex);
return '';
}
}
getAllPlaces() async {
HotScreen.places.removeRange(0, HotScreen.places.length);
Response response = await getRequest(devBaseURL + plainPlaceAPI);
var allPlacesMap = jsonDecode(response.body);
print(allPlacesMap.length);
for (int i = 0; i < allPlacesMap.length; i++) {
var json = allPlacesMap[i];
Place place = Place.fromJson(json);
setState(() {
HotScreen.places.add(place);
});
}
}
getAllItems() async {
HotScreen.items.removeRange(0, HotScreen.items.length);
Response response = await getRequest(devBaseURL + plainItemAPI);
var allItemsMap = jsonDecode(response.body);
for (int i = 0; i < allItemsMap.length; i++) {
var json = allItemsMap[i];
Item item = Item.fromJson(json);
setState(() {
HotScreen.items.add(item);
});
}
}
getAllPeople() async {
HotScreen.people.removeRange(0, HotScreen.people.length);
Response response = await getRequest(devBaseURL + plainPersonAPI);
var allPeopleMap = jsonDecode(response.body);
for (int i = 0; i < allPeopleMap.length; i++) {
var json = allPeopleMap[i];
Person person = Person.fromJson(json);
setState(() {
HotScreen.people.add(person);
});
}
}
#override
void initState() {
super.initState();
if(HotScreen.isFirstRun == true){
getAllItems();
getAllPlaces();
getAllPeople();
HotScreen.isFirstRun = false;
}
}
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: kPageMainBackgroundColorBoxDecoration,
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
//main screen scrollable widgets
child: ListView(
shrinkWrap: true,
children: <Widget>[
Text(
getTranslation('Bestiee'),
style: kBazarGalleryTitleStyle,
),
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text('Featured Items', style: kFeatureTitleTextStyle),
),
Container(
height: 700 / 3.5,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.items.length,
itemBuilder: (contet, int index) {
return SingleItemCard(
item: HotScreen.items[index],
isPersonItem: false,
moduleName: HotScreen.items[index].moduleName,
);
}),
),
SizedBox(
height: 10,
),
//more button
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(onPressed: () {
Navigator.pushNamed(context, MoreHotItemsScreen.id);
}),
),
),
SizedBox(
height: 20,
),
//second hand section
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text('Second Hand', style: kFeatureTitleTextStyle),
),
// Container(
// height: 700 / 3.5,
// child: ListView.builder(
// scrollDirection: Axis.horizontal,
// shrinkWrap: true,
// itemCount: 9,
// itemBuilder: (contet, int index) {
// return SingleItemCard();
// }),
// ),
SizedBox(
height: 10,
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotItemsScreen.id);
},
),
),
),
SizedBox(
height: 30,
),
//places section
Text(
'Featured Plces',
style: kFeatureTitleTextStyle,
),
Container(
height: 140,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.places.length,
itemBuilder: (context, int index) {
return Container(
width: 190,
height: 100,
child: SinglePlaceCard(
place: HotScreen.places[index],
));
},
),
),
//
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotPlacesScreen.id);
},
),
),
),
SizedBox(
height: 30,
),
//people section
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Text(
'People',
style: kFeatureTitleTextStyle,
),
),
Container(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: HotScreen.people.length,
itemBuilder: (context, int index) {
return Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePersonHotScreenCard(
person: HotScreen.people[index],
),
),
);
},
),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 80,
height: 30,
child: SmallRoundMoreButton(
onPressed: () {
Navigator.pushNamed(context, MoreHotPeopleScreen.id);
},
),
),
),
SizedBox(
height: 60,
),
//single shops section
Container(
height: 100.0 * 3,
child: Column(
children: List.generate(5, (index) {
return Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePlaceLargeHotCard(place: HotScreen.places[index],)
),
);
}),
)
),
],
),
),
),
),
);
}
}
code for SingleItemCard :
class SingleItemCard extends StatelessWidget {
SingleItemCard({
this.cardHeight = 300,
this.cardWidth = 200,
this.color = kAppOrangeColor,
this.isExtraInfoShowen = true,
this.item,
this.isPersonItem,
this.moduleName});
final double cardHeight;
final double cardWidth;
final Color color;
final bool isExtraInfoShowen;
final Item item;
final bool isPersonItem;
final String moduleName;
#override
Widget build(BuildContext context) {
String imageURL ;
//remove the null part in production
if(item.moduleName == 'item' || item.moduleName == null ){
imageURL = imageBaseURLPlaces + item.imageVariants[0].imageURL;
}else if (item.moduleName == 'person' ){
imageURL = imageBaseURLPeople + item.imageVariants[0].imageURL;
print(imageURL);
}
int ratingUserCount = item.ratingUserCount;
return FittedBox(
child: Padding(
padding: const EdgeInsets.only(right: 7),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SingleItemScreen(
item: item,
),
),
);
},
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
//upper item description
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Visibility(
visible: isExtraInfoShowen,
child: CircleRater(
isRatingDisabled: true,
rating: item.rating,
ratingUserCount: item.ratingUserCount != null ? ratingUserCount : 0,
),
),
SizedBox(
width: 150,
child: Text(
// 'Item Name Here no. 1',
item.name,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500),
softWrap: false,
),
),
//item card
Container(
height: cardHeight,
width: cardWidth,
child: Container(
width: 100,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
// child: Image.asset(
// 'images/item-image1.jpeg',
// width: 180,
// fit: BoxFit.fill,
// ),
child: CachedNetworkImage(imageUrl: imageURL, fit: BoxFit.fill,)
),
),
),
],
),
//lower price widget
Visibility(
visible: isExtraInfoShowen,
child: Align(
alignment: Alignment.bottomLeft,
child: Container(
width: 100,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
topLeft: Radius.circular(5)),
color: color),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 3, horizontal: 4),
child: Align(
alignment: Alignment.bottomRight,
child: Text(
// '1000000 IQD',
item.priceIQD.toString() + ' IQD',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
textAlign: TextAlign.start,
),
),
),
),
),
)
],
),
),
),
);
}
}
SinglePlaceCard :
class SinglePlaceCard extends StatelessWidget {
SinglePlaceCard({this.place});
Place place;
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double widgetWidth = screenWidth / 2.3;
int ratingUserCount = place.ratingUserCount;
String imageURL = place.coverImages != null
? imageBaseURLPlaces +
place.coverImages[0].imageURL
: '';
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SinglePlaceScreen(place: place, isScreenCalledFromOwnerSelfRegistrationScreen: false,),
),
);
},
child: FittedBox(
child: Padding(
padding: const EdgeInsets.only(right: 10),
child: Container(
child: Stack(
alignment: Alignment.bottomRight,
children: <Widget>[
Column(
//upper place name and rating
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CircleRater(
isRatingDisabled: true,
rating: place.rating,
ratingUserCount: place.ratingUserCount != null ? ratingUserCount : 0,
),
SizedBox(
width: widgetWidth,
child: Text(
place.name,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500),
softWrap: false,
),
),
//place card image
Container(
height: 80,
width: 170,
child: Container(
width: 150,
decoration: BoxDecoration(
color: kAppMainDarkGreenColor,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: place.coverImages == null
? Image.asset(
'images/blank-placeholder-images/blank-image-placeholder.png',
width: screenWidth / 4,
fit: BoxFit.fill,
)
: CachedNetworkImage(
imageUrl: imageURL,
fit: BoxFit.fill,
),
),
),
),
],
),
//lower tag
Align(
alignment: Alignment.bottomLeft,
child: Container(
width: 100,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(3),
bottomRight: Radius.circular(8)),
color: Colors.black54),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 3, horizontal: 4),
child: Align(
alignment: Alignment.bottomRight,
child: FittedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
EyeIconWithText(
size: 13,
),
Padding(
padding: const EdgeInsets.only(right: 5),
child: Container(
width: 10,
height: 10,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
color: place.isOnline != null &&
place.isOnline == true
? Colors.green
: Colors.red),
),
),
Text(
place.isOpen != null
? place.isOpen ? 'Open Now' : 'Colosed Now'
: 'Closed Now',
style: TextStyle(
fontSize: 12,
color: Colors.white,
),
textAlign: TextAlign.start,
),
],
),
),
),
),
),
)
],
),
),
),
),
);
}
}
SinglePersonHotScreenCard :
class SinglePersonHotScreenCard extends StatelessWidget {
SinglePersonHotScreenCard(
{ this.isExtraInfoShowen = false, this.person});
final bool isExtraInfoShowen;
final Person person;
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
final ratingPersonCount = person.ratingUserCount;
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, SinglePersonScreen.id);
},
child: FittedBox(
child: Column(
children: <Widget>[
//upper label and rating
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CircleRater(
isRatingDisabled: true,
rating: person.rating,
ratingUserCount: person.ratingUserCount != null ? ratingPersonCount : 0,
),
Text(
person.name,
style: TextStyle(color: Colors.black),
),
],
),
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.all(Radius.circular(1000)),
),
width: screenWidth / 3.6,
height: screenWidth / 3.6,
),
Visibility(
visible: person != null,
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(imageBaseURLPeople + person.profileImageURL,)
),
),
Visibility(
visible: person == null,
child: ClipOval(
child: SizedBox(
width: screenWidth / 4,
child:
Image.asset('images/blank-placeholder-images/person-placeholder.jpg'),
),
),
),
],
),
Text(
person.jobTitle,
style: TextStyle(color: Colors.black),
),
Padding(
padding: const EdgeInsets.only(top: 2.0),
child: Container(
width: 15,
height: 15,
decoration: kOnlineOfflineIndicatorBoxDecoration,
),
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Visibility(
visible: this.isExtraInfoShowen,
child: ThumbsUpWithLabel(),
),
)
],
),
),
),
);
}
}
I have ran the devtool, the app uses too much m memory, I am not sure why.
I changed the code there is a lot less crashes. I use this code
getAllPlaces() async {
List<Place> places = widget.places;
places.removeRange(0, places.length);
List<Place> _places = [];
Response response = await getRequest(devBaseURL + plainPlaceAPI);
var allPlacesMap = jsonDecode(response.body);
for (int i = 0; i < allPlacesMap.length; i++) {
var json = allPlacesMap[i];
Place place = Place.fromJson(json);
_places.add(place);
}
setState(() {
places.addAll(_places );
print('all added');
});
}
The app crash seems to be caused by Out Of Memory issue. Checking on the snippets you've provided, memory issues were caused by nested ListViews rendering Widgets that displays images. While the widgets in ListViews are only rendered when near the viewport, multiple ListViews still adds up to the total memory consumed. I suggest trimming down the images displayed and nest ListViews sparingly to prevent these issues.

How to set the text value dynamically from the json in flutter

Hi friends how to set the text value dynamically I am using the JSON to get the data but when I refresh the data is populating and I am calling the JSON at initstate to per load before the page of the app starts sorry friends I don't know much about the flutter so please help me out about it please find the code below
String name, userimage, birth, c_id, email, mobile_number;
class Profile extends StatefulWidget {
#override
State<StatefulWidget> createState() {
Profile_Customer profile_customer() => Profile_Customer();
return profile_customer();
}
}
class Profile_Customer extends State<Profile> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('Profile'),
backgroundColor: primarycolor,
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushReplacement(
context,
new MaterialPageRoute(
builder: (context) => new HomeScreen()));
}),
),
body: new Builder(builder: (BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Container(
child: new Image.asset('assets/rural_post_logo.png',
fit: BoxFit.cover),
margin: EdgeInsets.only(bottom: 15.0),
),
new Container(
child: new CircleAvatar(
child: new Image.network(userimage,
width: 100.0, height: 100.0, fit: BoxFit.cover),
),
margin: EdgeInsets.only(top: 10.0),
alignment: Alignment(0.0, 0.0),
),
new Container(
child: new Text(name),
margin: EdgeInsets.only(top: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Birth Date',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(birth),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Customer ID',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(c_id),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Email',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(email),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Mobile number',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(mobile_number),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new RaisedButton(
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new HomeScreen()));
},
color: secondarycolor,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(5.0)),
child: new Text('Update Profile',
style: new TextStyle(color: Colors.white)),
),
width: 300.0,
height: 40.0,
margin: EdgeInsets.only(top: 10.0),
)
],
);
}),
),
);
}
#override
void initState() {
super.initState();
profilejson();
}
}
void profilejson() async {
SharedPreferences pref = await SharedPreferences.getInstance();
var profile_url = url + "view_profile&userid=" + pref.getString('userid');
print(profile_url);
http.Response profileresponse = await http.get(profile_url);
var profile_response_json = json.decode(profileresponse.body);
name = profile_response_json['username'];
userimage = profile_response_json['image'];
birth = profile_response_json['dob'];
c_id = profile_response_json['customerid'];
email = profile_response_json['email'];
mobile_number = profile_response_json['phone'];
}
You can accomplish that with a StatefulWidget and setState to make the layout change on the go. As you already have the widget in your code you should simply call setState were you set your variables. Also the profilejson() should we within the state:
...
profilejson() async {
SharedPreferences pref = await SharedPreferences.getInstance();
var profile_url = url + "view_profile&userid=" + pref.getString('userid');
print(profile_url);
http.Response profileresponse = await http.get(profile_url);
var profile_response_json = json.decode(profileresponse.body);
// the variables you want the layout to be updated with
setState(() {
name = profile_response_json['username'];
userimage = profile_response_json['image'];
birth = profile_response_json['dob'];
c_id = profile_response_json['customerid'];
email = profile_response_json['email'];
mobile_number = profile_response_json['phone'];
})
}
#override
void initState() {
super.initState();
profilejson();
}
...
Full code:
String name, userimage, birth, c_id, email, mobile_number;
class Profile extends StatefulWidget {
#override
State<StatefulWidget> createState() {
Profile_Customer profile_customer() => Profile_Customer();
return profile_customer();
}
}
class Profile_Customer extends State<Profile> {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('Profile'),
backgroundColor: primarycolor,
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushReplacement(
context,
new MaterialPageRoute(
builder: (context) => new HomeScreen()));
}),
),
body: email != null ? new Builder(builder: (BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Container(
child: new Image.asset('assets/rural_post_logo.png',
fit: BoxFit.cover),
margin: EdgeInsets.only(bottom: 15.0),
),
new Container(
child: new CircleAvatar(
child: new Image.network(userimage,
width: 100.0, height: 100.0, fit: BoxFit.cover),
),
margin: EdgeInsets.only(top: 10.0),
alignment: Alignment(0.0, 0.0),
),
new Container(
child: new Text(name),
margin: EdgeInsets.only(top: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Birth Date',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(birth),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Customer ID',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(c_id),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Email',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(email),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Divider(
height: 2.0,
color: primarycolor,
),
margin: EdgeInsets.only(right: 10.0, left: 10.0, top: 10.0),
),
new Container(
child: new Text(
'Mobile number',
style: new TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0),
),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new Text(mobile_number),
alignment: Alignment(-1.0, 0.0),
margin: EdgeInsets.only(top: 10.0, left: 10.0),
),
new Container(
child: new RaisedButton(
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new HomeScreen()));
},
color: secondarycolor,
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(5.0)),
child: new Text('Update Profile',
style: new TextStyle(color: Colors.white)),
),
width: 300.0,
height: 40.0,
margin: EdgeInsets.only(top: 10.0),
)
],
);
}) : new Center(child: new CircularProgressIndicator()),
),
);
}
profilejson() async {
SharedPreferences pref = await SharedPreferences.getInstance();
var profile_url = url + "view_profile&userid=" + pref.getString('userid');
print(profile_url);
http.Response profileresponse = await http.get(profile_url);
var profile_response_json = json.decode(profileresponse.body);
// the variables you want the layout to be updated with
setState(() {
name = profile_response_json['username'];
userimage = profile_response_json['image'];
birth = profile_response_json['dob'];
c_id = profile_response_json['customerid'];
email = profile_response_json['email'];
mobile_number = profile_response_json['phone'];
})
}
#override
void initState() {
super.initState();
profilejson();
}
}

How do I stack widgets overlapping each other in flutter

I need to stack widgets like this:
I wrote the code below. However the coins are coming one after another with some default padding. How can I get something like the image above?
Row(
children: <Widget>[
Icon(
Icons.monetization_on, size: 36.0,
color: const Color.fromRGBO(218, 165, 32, 1.0),
),
Icon(
Icons.monetization_on, size: 36.0,
color: const Color.fromRGBO(218, 165, 32, 1.0),
),
],
),
You can use a Stack with Positioned to achieve this:
class StackExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: new Container(
padding: const EdgeInsets.all(8.0),
height: 500.0,
width: 500.0,
// alignment: FractionalOffset.center,
child: new Stack(
//alignment:new Alignment(x, y)
children: <Widget>[
new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
new Positioned(
left: 20.0,
child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
),
new Positioned(
left:40.0,
child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0)),
)
],
),
),
)
;
}
}
And this how you get some nice shadow drop so the icon stands out more:
class StackExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: new Container(
padding: const EdgeInsets.all(8.0),
height: 500.0,
width: 500.0,
// alignment: FractionalOffset.center,
child: new Stack(
//alignment:new Alignment(x, y)
children: <Widget>[
new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
boxShadow: [
new BoxShadow(
blurRadius: 5.0,
offset: const Offset(3.0, 0.0),
color: Colors.grey,
)
]
),
child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
new Positioned(
left: 20.0,
child: new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
boxShadow: [
new BoxShadow(
blurRadius: 5.0,
offset: const Offset(3.0, 0.0),
color: Colors.grey,
)
]
),
child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
),
new Positioned(
left:40.0,
child: new Container(
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(25.0),
boxShadow: [
new BoxShadow(
blurRadius: 5.0,
offset: const Offset(3.0, 0.0),
color: Colors.grey,
)
]
)
,child: new Icon(Icons.monetization_on, size: 36.0, color: const Color.fromRGBO(218, 165, 32, 1.0))),
)
],
),
),
)
;
}
}
As of November 2019 I'd like to add a second solution:
Using package: https://pub.dev/packages/assorted_layout_widgets
var widget1 = ...;
var widget2 = ...;
RowSuper(
children: [widget1, widget2],
innerDistance: -20.0,
);
This will overlap row cells by 20 pixels.
The difference from this solution to the one using Stack is that Positioned widgets in a Stack don't occupy space. So you can't make the Stack the size of its contents, unless you know their sizes in advance. However, the RowSuper will have the size of all of its children widgets.
Note, there is also a ColumnSuper. Also note I am the author of this package.
I wanted something without dependencies and without hardcoded layout.
You could enhance by making overlap use a media query to overlap in terms of %.
Widget overlapped() {
final overlap = 10.0;
final items = [
CircleAvatar(child: Text('1'), backgroundColor: Colors.red),
CircleAvatar(child: Text('2'), backgroundColor: Colors.green),
CircleAvatar(child: Text('3'), backgroundColor: Colors.blue),
];
List<Widget> stackLayers = List<Widget>.generate(items.length, (index) {
return Padding(
padding: EdgeInsets.fromLTRB(index.toDouble() * overlap, 0, 0, 0),
child: items[index],
);
});
return Stack(children: stackLayers);
}
Here is my code on Profile Picture overlapped by camera image in flutter.
Output:
Click here to view output image
Container(
constraints: new BoxConstraints(
maxHeight: 200.0,
maxWidth: 200.0
),
padding: new EdgeInsets.only(left: 16.0, bottom: 8.0, right: 16.0),
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
image: new AssetImage('assets/images/profile.png'),
fit: BoxFit.cover,
),
),
child: Stack(
children: [
new Positioned(
right: 0.0,
bottom: 3.0,
child: Container(
constraints: new BoxConstraints(
maxHeight: 50.0,
maxWidth: 50.0
),
decoration: new BoxDecoration(
boxShadow: [
BoxShadow(
color:Color(0xFFdedede),
offset: Offset(2,2)
),
],
color: Colors.white,
shape: BoxShape.circle,
),
child: GestureDetector(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.photo_camera,
size: 34,
color: Color(0xFF00cde7),
),
),
),
),
),
],
),
)
Wraping your elements with OverflowBox and giving the maxWidth value will achieve this effect.
The following can be used in a row or a listview
return SizedBox(
width: 35, //--> list children will be 35 in width
child: OverflowBox(
maxWidth: 50, // --> allowing the child to overflow will cause overlap between elements
child: Container(
width: 50,
child: Text((index + 1).toString()),
),
),
);
You could try my package (signed_spacing_flex). It's exactly the same as a normal Row (or Column and Flex). But it also lets you set negative spacing which causes its children to overlap. You can also set which children should be on top when they overlap.
In your case it would be something like:
SignedSpacingRow(
spacing: -12.0,
stackingOrder: StackingOrder.lastOnTop,
children: <Widget>[
Icon(
Icons.monetization_on, size: 36.0,
color: const Color.fromRGBO(218, 165, 32, 1.0),
),
Icon(
Icons.monetization_on, size: 36.0,
color: const Color.fromRGBO(218, 165, 32, 1.0),
),
],
),
It also works with expanded children if you need.
Reverse variant based on #Lee Higgins
const size = 32.0;
const overlap = size - 6.0;
final containers = [
Container(
decoration: BoxDecoration(
color: Colors.grey[300],
shape: BoxShape.circle,
),
width: size,
height: size,
),
Container(
decoration: BoxDecoration(
color: Colors.grey[400],
shape: BoxShape.circle,
),
width: size,
height: size,
),
];
List<Widget> stackLayers = List<Widget>.generate(containers.length, (index) {
return Padding(
padding: EdgeInsets.fromLTRB(0, 0, index * overlap, 0),
child: containers[index],
);
});
return Stack(alignment: AlignmentDirectional.topEnd, children: stackLayers);
Stack is very much confusing.
The best solution is to use enter link description here

Flutter: Create a timeline UI

I'm trying to make timeline UI like below:
But i end up doing following:
I want to increase the height of vertical Separator when no of lines of my Description text increases. How should i do that ?
Here is the link for the code
I like Osama's answer too but here's my quick custom implementation. It uses a CustomPainter to draw the lines.
import 'package:flutter/material.dart';
class Timeline extends StatelessWidget {
const Timeline({
Key? key,
required this.children,
this.indicators,
this.isLeftAligned = true,
this.itemGap = 12.0,
this.gutterSpacing = 4.0,
this.padding = const EdgeInsets.all(8),
this.controller,
this.lineColor = Colors.grey,
this.physics,
this.shrinkWrap = true,
this.primary = false,
this.reverse = false,
this.indicatorSize = 30.0,
this.lineGap = 4.0,
this.indicatorColor = Colors.blue,
this.indicatorStyle = PaintingStyle.fill,
this.strokeCap = StrokeCap.butt,
this.strokeWidth = 2.0,
this.style = PaintingStyle.stroke,
}) : itemCount = children.length,
assert(itemGap >= 0),
assert(lineGap >= 0),
assert(indicators == null || children.length == indicators.length),
super(key: key);
final List<Widget> children;
final double itemGap;
final double gutterSpacing;
final List<Widget>? indicators;
final bool isLeftAligned;
final EdgeInsets padding;
final ScrollController? controller;
final int itemCount;
final ScrollPhysics? physics;
final bool shrinkWrap;
final bool primary;
final bool reverse;
final Color lineColor;
final double lineGap;
final double indicatorSize;
final Color indicatorColor;
final PaintingStyle indicatorStyle;
final StrokeCap strokeCap;
final double strokeWidth;
final PaintingStyle style;
#override
Widget build(BuildContext context) {
return ListView.separated(
padding: padding,
separatorBuilder: (_, __) => SizedBox(height: itemGap),
physics: physics,
shrinkWrap: shrinkWrap,
itemCount: itemCount,
controller: controller,
reverse: reverse,
primary: primary,
itemBuilder: (context, index) {
final child = children[index];
final _indicators = indicators;
Widget? indicator;
if (_indicators != null) {
indicator = _indicators[index];
}
final isFirst = index == 0;
final isLast = index == itemCount - 1;
final timelineTile = <Widget>[
CustomPaint(
foregroundPainter: _TimelinePainter(
hideDefaultIndicator: indicator != null,
lineColor: lineColor,
indicatorColor: indicatorColor,
indicatorSize: indicatorSize,
indicatorStyle: indicatorStyle,
isFirst: isFirst,
isLast: isLast,
lineGap: lineGap,
strokeCap: strokeCap,
strokeWidth: strokeWidth,
style: style,
itemGap: itemGap,
),
child: SizedBox(
height: double.infinity,
width: indicatorSize,
child: indicator,
),
),
SizedBox(width: gutterSpacing),
Expanded(child: child),
];
return IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children:
isLeftAligned ? timelineTile : timelineTile.reversed.toList(),
),
);
},
);
}
}
class _TimelinePainter extends CustomPainter {
_TimelinePainter({
required this.hideDefaultIndicator,
required this.indicatorColor,
required this.indicatorStyle,
required this.indicatorSize,
required this.lineGap,
required this.strokeCap,
required this.strokeWidth,
required this.style,
required this.lineColor,
required this.isFirst,
required this.isLast,
required this.itemGap,
}) : linePaint = Paint()
..color = lineColor
..strokeCap = strokeCap
..strokeWidth = strokeWidth
..style = style,
circlePaint = Paint()
..color = indicatorColor
..style = indicatorStyle;
final bool hideDefaultIndicator;
final Color indicatorColor;
final PaintingStyle indicatorStyle;
final double indicatorSize;
final double lineGap;
final StrokeCap strokeCap;
final double strokeWidth;
final PaintingStyle style;
final Color lineColor;
final Paint linePaint;
final Paint circlePaint;
final bool isFirst;
final bool isLast;
final double itemGap;
#override
void paint(Canvas canvas, Size size) {
final indicatorRadius = indicatorSize / 2;
final halfItemGap = itemGap / 2;
final indicatorMargin = indicatorRadius + lineGap;
final top = size.topLeft(Offset(indicatorRadius, 0.0 - halfItemGap));
final centerTop = size.centerLeft(
Offset(indicatorRadius, -indicatorMargin),
);
final bottom = size.bottomLeft(Offset(indicatorRadius, 0.0 + halfItemGap));
final centerBottom = size.centerLeft(
Offset(indicatorRadius, indicatorMargin),
);
if (!isFirst) canvas.drawLine(top, centerTop, linePaint);
if (!isLast) canvas.drawLine(centerBottom, bottom, linePaint);
if (!hideDefaultIndicator) {
final Offset offsetCenter = size.centerLeft(Offset(indicatorRadius, 0));
canvas.drawCircle(offsetCenter, indicatorRadius, circlePaint);
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
You'd call it something like:
Timeline(
children: <Widget>[
Container(height: 100, color: color),
Container(height: 50, color: color),
Container(height: 200, color: color),
Container(height: 100, color: color),
],
indicators: <Widget>[
Icon(Icons.access_alarm),
Icon(Icons.backup),
Icon(Icons.accessibility_new),
Icon(Icons.access_alarm),
],
),
new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Stack(
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(left: 50.0),
child: new Card(
margin: new EdgeInsets.all(20.0),
child: new Container(
width: double.infinity,
height: 200.0,
color: Colors.green,
),
),
),
new Positioned(
top: 0.0,
bottom: 0.0,
left: 35.0,
child: new Container(
height: double.infinity,
width: 1.0,
color: Colors.blue,
),
),
new Positioned(
top: 100.0,
left: 15.0,
child: new Container(
height: 40.0,
width: 40.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: new Container(
margin: new EdgeInsets.all(5.0),
height: 30.0,
width: 30.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.red),
),
),
)
],
);
},
itemCount: 5,
)
the output will be like this image
For those who are scrolling here to find an easy way to implement timelines, now you can do this easily with timeline_tile.
Check out this specific delivery layout:
Or this weather timeline:
Also, the beautiful_timelines repository contains some examples built with this package.
Web demo
class MyTimeLine extends StatefulWidget {
#override
_TimeLineState createState() => _TimeLineState();
}
class _TimeLineState extends State<MyTimeLine> {
#override
Widget build(BuildContext context) {
return new Padding(
padding: new EdgeInsets.symmetric(horizontal: 10.0),
child: new Column(
children: <Widget>[
IntrinsicHeight(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Wrap(
direction: Axis.vertical,
children: <Widget>[
new Container(
width: 30.0,
child: new Center(
child: new Stack(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(left: 12.0),
child: new Container(
margin:
new EdgeInsets.symmetric(vertical: 4.0),
height: double.infinity,
width: 1.0,
color: Colors.deepOrange),
),
new Container(
padding: new EdgeInsets.only(),
child: new Icon(Icons.star, color: Colors.white),
decoration: new BoxDecoration(
color: new Color(0xff00c6ff),
shape: BoxShape.circle),
)
],
),
),
),
],
),
new Expanded(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(left: 20.0, top: 5.0),
child: new Text(
'Header Text',
style: new TextStyle(
fontWeight: FontWeight.w500,
color: Colors.deepOrange,
fontSize: 16.0),
),
),
new Padding(
padding: new EdgeInsets.only(left: 20.0, top: 5.0),
child: new Text(
'Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here Lorem ipsum description here description here '),
)
],
),
)
],
),
)
],
),
);
}
}
Check out this pub.
https://pub.dev/packages/timeline_widget
It helps create beautiful timeline ui with everything customised. Also available in left right and center aligned options. This is an example of how to use it!
TimelineView(
align: TimelineAlign.rightAlign,
lineWidth: 4,
lineColor: Colors.deepOrange,
imageBorderColor: Colors.deepOrange,
image: [
Container(
padding: EdgeInsets.all(15),
child: Image.asset("assets/pre-breakfast-image.png")),
Container(
padding: EdgeInsets.all(15),
child: Image.asset("assets/breakfast-image.png")),
Container(
padding: EdgeInsets.all(15),
child: Image.asset("assets/pre-lunch-image.png")),
Container(
padding: EdgeInsets.all(15),
child: Image.asset("assets/lunch-image.png")),
Container(
padding: EdgeInsets.all(15),
child: Image.asset("assets/evening-snack-image.png")),
Container(
padding: EdgeInsets.all(20),
child: Image.asset("assets/dinner-image.png")),
],
height: 150,
width: MediaQuery.of(context).size.width,
imageHeight: 50,
children: [
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(20, 71, 31)),
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(15, 75, 55)),
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(25, 73, 30)),
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(22, 65, 35)),
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(21, 55, 32)),
Container(
margin: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: _widgetWeather(20, 65, 35)),
],
),
you can use flutter_timeline, both body and indicator, lines are customizable.
https://github.com/softmarshmallow/flutter-timeline
this is the latest, supported package out there for flutter.

Flutter - Float Action Button - Hiding the visibility of items

I'm trying to control the visibility of the smaller items of the FAB, according to the gif below:
However I am not able to insert the opacity in the items. Anywhere I put some kind of error occurs. I do not know if opacity is the best way to do it.
To hide the items I believe that with animation it is possible to control the time in which they will appear.
Below is what I have achieved so far:
Could you help me solve this problem?
Below is the above gif code:
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
int _angle = 90;
bool _isRotated = true;
void _rotate(){
setState((){
if(_isRotated) {
_angle = 45;
_isRotated = false;
} else {
_angle = 90;
_isRotated = true;
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 200.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo1',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFF9E9E9E),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 144.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo2',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFF00BFA5),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 88.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo3',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
],
),
)
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 56.0,
height: 56.00,
child: new InkWell(
onTap: _rotate,
child: new Center(
child: new RotationTransition(
turns: new AlwaysStoppedAnimation(_angle / 360),
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
)
),
)
),
)
),
),
]
)
);
}
}
I used Opacity but it became redundant after using ScaleTransition. With ScaleTransition it is possible to hide and show widgets.
I used 3 animations to be able to generate the cascade effect.
Below is the code and its result:
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new HomePage()));
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage> with TickerProviderStateMixin {
int _angle = 90;
bool _isRotated = true;
AnimationController _controller;
Animation<double> _animation;
Animation<double> _animation2;
Animation<double> _animation3;
#override
void initState() {
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 180),
);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
_animation2 = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.5, 1.0, curve: Curves.linear),
);
_animation3 = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.8, 1.0, curve: Curves.linear),
);
_controller.reverse();
super.initState();
}
void _rotate(){
setState((){
if(_isRotated) {
_angle = 45;
_isRotated = false;
_controller.forward();
} else {
_angle = 90;
_isRotated = true;
_controller.reverse();
}
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Stack(
children: <Widget>[
new Positioned(
bottom: 200.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation3,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo1',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation3,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFF9E9E9E),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo1");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 144.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation2,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo2',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation2,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFF00BFA5),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo2");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 88.0,
right: 24.0,
child: new Container(
child: new Row(
children: <Widget>[
new ScaleTransition(
scale: _animation,
alignment: FractionalOffset.center,
child: new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Text(
'foo3',
style: new TextStyle(
fontSize: 13.0,
fontFamily: 'Roboto',
color: new Color(0xFF9E9E9E),
fontWeight: FontWeight.bold,
),
),
),
),
new ScaleTransition(
scale: _animation,
alignment: FractionalOffset.center,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 40.0,
height: 40.0,
child: new InkWell(
onTap: (){
if(_angle == 45.0){
print("foo3");
}
},
child: new Center(
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
),
)
),
)
),
),
],
),
)
),
new Positioned(
bottom: 16.0,
right: 16.0,
child: new Material(
color: new Color(0xFFE57373),
type: MaterialType.circle,
elevation: 6.0,
child: new GestureDetector(
child: new Container(
width: 56.0,
height: 56.00,
child: new InkWell(
onTap: _rotate,
child: new Center(
child: new RotationTransition(
turns: new AlwaysStoppedAnimation(_angle / 360),
child: new Icon(
Icons.add,
color: new Color(0xFFFFFFFF),
),
)
),
)
),
)
),
),
]
)
);
}
}
you can see Flutter floating action button with speed dial
or
use https://pub.dartlang.org/packages/flutter_speed_dial
or
flutter Visibility widget
or
https://medium.com/#agungsurya/create-a-simple-animated-floatingactionbutton-in-flutter-2d24f37cfbcc

Resources