Good morning,
I'm new on Flutter.
I need to open a new page from a card after a button press. To open the new page I need the card data object that I use to show the information (data[X]). The button for open the new page is located in ButtonTheme.bar but in this location I don't have my "data[X]" object.
This is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text("Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(context, new MaterialPageRoute(builder: (__) => new PeriodEditPage(toChangeData: //my card data? ,)));
},
),
],
),
),
],
),
);
}
}));
}
I simply need to have the specific card data when I'm going to open the new page.
Hope I was clear.
Thank you
The trick is to declare your data in your statefulWidget (or statelessWidget) then use then wherever you want.
You can also create an object data where you'll have all your information instanciate one into your widget then pass it to another screen.
Here's an intro about passing data : https://flutter.dev/docs/cookbook/navigation/passing-data
Finally I found the solution to my problem:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Records Page", style: TextStyle(color: Colors.white)),
iconTheme: new IconThemeData(color: Colors.white),
backgroundColor: Color.fromRGBO(32, 38, 48, 1),
),
drawer: MainDrawer(),
body: new ListView.builder(
//reverse: true,
itemCount: reversedData != null ? reversedData.length : 0,
itemBuilder: (BuildContext ctxt, int index) {
if (reversedData[index].stop != null) {
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.calendar_today),
title: Text('Last Period'),
//subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
),
new Container(
margin: EdgeInsets.symmetric(horizontal: 15),
child: new ListView(
shrinkWrap: true,
children: <Widget>[
new Text(
"Hour: ${reversedData[index].hourType.description}"),
new Text(
"Job: ${reversedData[index].job.description}"),
new Text(
"Opened at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].start))}"),
new Text(
"Closed at: ${DateFormat.yMd().add_jm().format(DateTime.parse(reversedData[index].stop))}"),
],
),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Modify',
style: TextStyle(
color: Color.fromRGBO(32, 38, 48, 1))),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (__) => new PeriodEditPage(
toChangeData: reversedData[index],
)));
},
),
],
),
),
],
),
);
}
}));
}
My problem was solved thank to this line:
toChangeData: reversedData[index],
This code permit to open every card details with the same object used to populate my card.
I haven't understand properly what happen with the object reference but it works for me.
Related
I have a ListView in which I will dynamically add in some children of same type. Inside every children widget has a button. What I want to implement is, that, when user presses the button on a child widget, this child widget will be removed from the ListView. I can do this in C# using events, but I'm a total noob to Dart and Flutter.
Here is my ListView
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Edit Plan'),
backgroundColor: Colors.green,
actions: <Widget>[
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
txts.add('set');
});
},
),
)
],
),
backgroundColor: Colors.white,
body: ListView(
children: txts.map((string) =>
new ListViewItem()).toList(growable: false),
),
);
}
And here is my listViewItem:
class ListViewItem extends StatelessWidget {
final Workout workout;
ListViewItem({Key key, #required this.workout})
: assert(workout != null),
super(key: key);
#override
Widget build(BuildContext context) {
// TODO: implement build
final theme = Theme.of(context);
return Padding(
padding: EdgeInsets.all(12),
child: Card(
elevation: 12,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text(
'The Enchanted Nightingale',
style: TextStyle(color: Colors.white),
),
subtitle: Text(
'Music by Julie Gable. Lyrics by Sidney Stein.',
style: TextStyle(color: Colors.white),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Name your workout',
labelStyle: TextStyle(color: Colors.white)),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'DELETE',
style: TextStyle(color: Colors.white),
),
onPressed: () {},
),
],
),
),
],
),
)),
);
}
}
I edited your code to use a ListView.builder, you need to remove the item at index from the List (txts) you are using, your code will be as follows:
List<String> txts = List();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Plan'),
backgroundColor: Colors.green,
actions: <Widget>[
Builder(
builder: (context) =>
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
txts.add('set');
});
},
),
)
],
),
backgroundColor: Colors.white,
body: new ListView.builder(
itemCount: txts.length,
itemBuilder: (BuildContext context, int index) {
return ListViewItem(
workout: workout,
onDelete: (){
setState(() {
txts.removeAt(index);
});
},
);
},
),
);
}
in addition to that you need to add an ondelete callback in the ListViewItem, the code in the ListViewItem class will be as follows:
class ListViewItem extends StatelessWidget {
final Workout workout;
final VoidCallback onDelete;
ListViewItem({Key key, #required this.workout, this.onDelete})
: assert(workout != null),
super(key: key);
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: EdgeInsets.all(12),
child: Card(
elevation: 12,
color: Colors.green,
child: Padding(
padding: EdgeInsets.only(top: 4, bottom: 4, left: 8, right: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: Icon(Icons.album),
title: Text(
'The Enchanted Nightingale',
style: TextStyle(color: Colors.white),
),
subtitle: Text(
'Music by Julie Gable. Lyrics by Sidney Stein.',
style: TextStyle(color: Colors.white),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Name your workout',
labelStyle: TextStyle(color: Colors.white)),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text(
'DELETE',
style: TextStyle(color: Colors.white),
),
onPressed: () =>onDelete(),
),
],
),
),
],
),
)),
);
}
}
I am consulting the news section of my website.
I'm using Future Builder to get the data from the web.
The problem I get is related to the image that I try to show on the screen.
And when there is a lot of news, the data load takes a long time and I do not know if there is a solution for loading faster.
I am consulting the text of the news through a json.
At that moment you get the URL of another JSON where the image is in thumbnail format.
I hope to solve this problem, I appreciate any help.
News.dart - Code
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: searchBar.build(context),
key: _scaffoldKey,
body: new Container(
color: Colors.grey[800],
child: new RefreshIndicator(
child: new ListView(
children: <Widget>[
new FutureBuilder<List<Post>>(
future: fetchPosts(URLWEB),
builder: (context, snapshot) {
if(snapshot.hasData) {
List<Post> posts = snapshot.data;
return new Column(
children: posts.map((post2) => new Column(
children: <Widget>[
new Card(
margin: new EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0),
color: Colors.white,
child: new GestureDetector(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new FutureBuilder(
future: fetchPostsIMG(post2.imagen),
builder: (context, AsyncSnapshot<PostImg> snapshot2){
return new Container(
height: 200.0,
decoration: new BoxDecoration(
image: new DecorationImage(
image: CachedNetworkImageProvider(snapshot2.data.imagen == null ? new AssetImage('images/logotipo.png') : snapshot2.data.imagen),
fit: BoxFit.fitWidth
)
),
width: MediaQuery.of(context).size.width,
);
},
),
new ListTile(
title: new Text(post2.titulo.replaceAll("‘", "").replaceAll(
"’", "").replaceAll("–", "")
.replaceAll("…", "").replaceAll(
"”", "")
.replaceAll("“", ""),
style: new TextStyle(
color: Colors.black,
fontSize: 18.0,
fontWeight: FontWeight.bold),),
subtitle: new HtmlView(data: post2.informacion),
dense: true,
)
],
),
onTap: () {
//Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context)=> new WebView(url: post2.urlweb, titulo: titulo)));
},
)
)
],
)).toList(),
);
}
else if(snapshot.hasError)
{
return new Container();
}
return new Center(
child: new Column(
children: <Widget>[
new Padding(padding: new EdgeInsets.all(50.0)),
new CircularProgressIndicator(),
],
),
);
},
),
],
),
onRefresh: _autoRefresh
),
),
);
}
}
It's because you are trying to access imagen on null object. You can do hasData check like below
CachedNetworkImageProvider(snapshot2.hasData ? snapshot2.data.imagen : new AssetImage('images/logotipo.png')),
I'm trying to add a floating action button to an image. The button will be used to change the selected image. I want the button to float over the bottom right of the image. So far all I can do is add the floating action button directly below the image. Thanks for any help!
#override
Widget build(BuildContext context) {
// Create the view scaffold. Use a list view so things scroll.
return new Scaffold(
appBar: new AppBar(
title: new Text("My Title"),
),
body: new ListView(
children: [
new Container(
padding: EdgeInsets.zero,
child: new Image.asset(
"assets/images/background_image.png",
fit: BoxFit.fitWidth,
),
),
new FloatingActionButton(
child: const Icon(Icons.camera_alt),
backgroundColor: Colors.green.shade800,
onPressed: () {},
),
new Divider(),
new ListTile(
title: new Text('Email'),
leading: const Icon(Icons.email),
onTap: () {},
),
new Divider(),
],
),
);
}
You may use a Stack and Positioned widget, you can read about those widgets here:
Stack:
https://docs.flutter.io/flutter/widgets/Stack-class.html
Positioned:
https://docs.flutter.io/flutter/widgets/Positioned-class.html
return new Scaffold(
appBar: new AppBar(
title: new Text("My Title"),
),
body: new ListView(
children: [
Stack(
children: <Widget>[
new Container(
padding: EdgeInsets.zero,
child: new Image.asset(
"assets/images/background_image.png",
fit: BoxFit.fitWidth,
)),
Positioned(
right: 0.0,
bottom: 0.0,
child: new FloatingActionButton(
child: const Icon(Icons.camera_alt),
backgroundColor: Colors.green.shade800,
onPressed: () {},
),
),
],
),
new Divider(),
new ListTile(
title: new Text('Email'),
leading: const Icon(Icons.email),
onTap: () {},
),
new Divider(),
],
),
);
I use this method to show a AlertDialog:
_onSubmit(message) {
if (message.isNotEmpty) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Center(child: Text('Alert')),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children : <Widget>[
Expanded(
child: Text(
message,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
),
),
)
],
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('Ok'),
onPressed: () {
_inputTextController.clear();
Navigator.of(context).pop();
})
],
);
},
);
}
}
Everything is working but the buttons are aligned in right as shown on picture below:
I want to style some how the buttons, for example one on start other on end.
I searched in docs but only found how to make them "Stacked full-width buttons".
Any ideas how to style the buttons?
Update 2022/10/22
Flutter 2.5 introduced the actionsAlignment property:
AlertDialog(
title: ..
actions: ..
actionsAlignment: MainAxisAlignment.end
),
Customize widget
Edit the the widget itself: Under the AlertDialog there is a ButtonBar widget where you can use alignment: MainAxisAlignment.spaceBetween to align the buttons correctly. See this answer for an example of a custom AlertDialog widget.
Own button row
You could also remove the buttons under actions and add an own custom Row with RaisedButtons in it, somehow like this:
Row (
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RaisedButton(), // button 1
RaisedButton(), // button 2
]
)
In your case you could add a Column around the Row in content and in there add your existing Row and the modified one you created from the above example.
Move buttons to content is a good solution.
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Center(child: Text('Alert')),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
child: Text(
"message",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
child: Text('Yes'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
})
])
],
),
);
});
Changing the theme is a good option.
MaterialApp(
theme: ThemeData(
buttonBarTheme: ButtonBarThemeData(
alignment: MainAxisAlignment.center,
)),
...
Don't add button in actions of AlertDialog. As you can see.
_onSubmit(message) {
if (message.isNotEmpty) {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Center(child: Text('Alert')),
content: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children : <Widget>[
Expanded(
child: Text(
message,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
),
),
),
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('Ok'),
onPressed: () {
_inputTextController.clear();
Navigator.of(context).pop();
})
],
),
);
},
);
}
}
I use this method to align buttons in actions of AlertDialog.
How this works::
The SizedBox takes the full width of the alertdialog using its context(In the statement MediaQuery.of(context).size.width. After that, a row is used which places space between its children in the main axis(mainAxisAlignment => the x-axis for a row).
AlertDialog style:
AlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Title'),
CloseButton(
color: Color(0xFFD5D3D3),
onPressed: () {
Navigator.of(context).pop();
})
]),
content: SingleChildScrollView(child: Text("Boby")),
actions: [
SizedBox(
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ButtonTheme(
minWidth: 25.0,
height: 25.0,
child: OutlineButton(
borderSide: BorderSide(color: Colors.blueAccent),
child: new Text("Save"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
onPressed: () {})),
SizedBox(width: 8.0),
ButtonTheme(
minWidth: 25.0,
height: 25.0,
child: OutlineButton(
borderSide: BorderSide(color: Colors.black26),
textColor: Colors.blue,
child: new Text("Close"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
onPressed: () {}))
]))
]);
Here is the straightforward answer for your problem:
Just use actionsAlignment property of AlertDialog class in flutter. Like so
AlertDialog(
actions: ...,
actionsAlignment: MainAxisAlignment.spaceBetween
)
Or you can use RFlutter Alert library for that. It is easily customizable and easy-to-use. Its default style includes rounded corners and you can add buttons as much as you want.
Alert Style:
var alertStyle = AlertStyle(
animationType: AnimationType.fromTop,
isCloseButton: false,
isOverlayTapDismiss: false,
descStyle: TextStyle(fontWeight: FontWeight.bold),
animationDuration: Duration(milliseconds: 400),
alertBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0.0),
side: BorderSide(
color: Colors.grey,
),
),
titleStyle: TextStyle(
color: Colors.red,
),
);
And assing your AlertStyle object to Alert's style field.
Alert(
context: context,
style: alertStyle,
type: AlertType.info,
title: "RFLUTTER ALERT",
desc: "Flutter is more awesome with RFlutter Alert.",
buttons: [
DialogButton(
child: Text(
"COOL",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
color: Color.fromRGBO(0, 179, 134, 1.0),
radius: BorderRadius.circular(0.0),
),
],
).show();
*I'm one of developer of RFlutter Alert.
new Expanded(
child: _searchResult.length != 0 || controller.text.isNotEmpty
? new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, int i) {
return new Card(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Row(children: <Widget>[
//new GestureDetector(),
new Container(
width: 45.0,
height: 45.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.fill,
image: new NetworkImage(
"https://raw.githubusercontent.com/flutter/website/master/_includes/code/layout/lakes/images/lake.jpg")))),
new Text(
" " +
userDetails[returnTicketDetails[i]
["user_id"]]["first_name"] +
" " +
(userDetails[returnTicketDetails[i]
["user_id"]]["last_name"]),
style: const TextStyle(
fontFamily: 'Poppins', fontSize: 20.0)),
]),
new Column(
children: <Widget>[
new Align(
alignment: FractionalOffset.topRight,
child: new FloatingActionButton(
onPressed: () {
groupId = returnTicketDetails[i]["id"];
print(returnTicketDetails[i]["id"]);
print(widget.id);
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Tickets(groupId,widget.id)));
},
heroTag: null,
backgroundColor: Color(0xFF53DD6C),
child: new Icon(Icons.arrow_forward),
)),
new Padding(padding: new EdgeInsets.all(3.0)),
],
)
]));
},
)
: new ListView.builder(
itemCount: _searchResult.length,
itemBuilder: (context, int i) {
return new Card(
child: new ListTile(
//title: new Text(userDetails[returnTicketDetails[i]["user_id"]]["first_name"]),
),
margin: const EdgeInsets.all(0.0),
);
},
),
),
Hi everyone! As I am building dynamically a Card in a ListView, I was thinking rather than keep the FloatingActionButton in each of them as I already do, to implement a onTap method in each card and trigger something.
In other words, I would like to keep the card as simple as possible without many widget around.
Thank you in advance!
As Card is "a sheet of Material", you probably want to use InkWell, which includes Material highlight and splash effects, based on the closest Material ancestor.
return Card(
child: InkWell(
onTap: () {
// Function is executed on tap.
},
child: ..,
),
);
You should really be wrapping the child in InkWell instead of the Card:
return Card(
child: InkWell(onTap: () {},
child: Text("hello")));
This will make the splash animation appear correctly inside the card rather than outside of it.
Just wrap the Card with GestureDetector as below,
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new ListView.builder(
itemBuilder: (context, i) {
new GestureDetector(
child: new Card(
....
),
onTap: onCardTapped(i),
);
},
);
}
onCardTapped(int position) {
print('Card $position tapped');
}
}