Related
Essentially I want to do this but instead of updating a single image, I want to update a list of images. I have a ListView of images based off a List<String> of image URLs. See below:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
double padding = 5.0;
String one = 'https://SOME_URL_HERE.com';
String two = 'https://SOME_URL_HERE.com';
String three = 'https://SOME_URL_HERE.com';
String four = 'https://SOME_URL_HERE.com';
List urls;
#override
Widget build(BuildContext context) {
urls = List();
urls.add(one);
urls.add(two);
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: new Scaffold(
appBar: new AppBar(
title: new Text("Image list test"),
),
body: showBody(),
),
);
}
Widget showBody() {
return Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, right: 10.0),
child: ListView(
children: <Widget>[
showItemImages(),
RaisedButton(
child: Text("Change image"),
onPressed: () {
setState(() {
urls = List();
urls.add(three);
urls.add(four);
});
},
),
Container(
child: Text('urls length: ${urls.length}'),
),
],
),
);
}
Widget showItemImages() {
return Padding(
padding: EdgeInsets.all(padding),
child: Container(
margin: EdgeInsets.symmetric(vertical: 20.0),
height: 200,
child: urls.length > 0 //item.images.length > 0
? getImagesListView(context)
: Container(child: Text('No images yet'))),
);
}
getImagesListView(BuildContext context) {
return ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: urls.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
width: 160.0,
child: _sizedContainer(
new CachedNetworkImage(
key: new ValueKey<String>(urls[index]),
imageUrl: urls[index],
//placeholder: (context, url) => new CircularProgressIndicator(),
),
),
);
},
);
}
Widget _sizedContainer(Widget child) {
return new SizedBox(
width: 300.0,
height: 150.0,
child: new Center(
child: child,
),
);
}
}
When I push the button, it should clear the list and add two new elements to it but it is not working
The following 3 lines:
urls = List();
urls.add(one);
urls.add(two);
should not be put inside #override widget build method. putting them there means that you re-initialize urls every time you setstate, you should put these lines inside #override initState() method.
I use flutter_local_notifications package to set notifications. I have an expandable list and every option of a title has a star icon. When I press one of white star icons, its color changes and set a notification("_showNotification" method).
In case that I press two or more stars, my app shows only last notification, but I want to show all of them. How can I do this?
This is whole code:
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
void main() {
runApp(new MaterialApp(home: new Home()));
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Expandable List"),
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ExpandableListView(ind: index, title: broadcast[index].title);
},
itemCount: 2,
),
);
}
}
class ExpandableListView extends StatefulWidget {
final String title;
final int ind;
const ExpandableListView({this.ind, this.title});
#override
_ExpandableListViewState createState() => new _ExpandableListViewState();
}
class _ExpandableListViewState extends State<ExpandableListView> {
bool expandFlag = false;
Color _iconColor = Colors.white;
#override
Widget build(BuildContext context) {
return new Container(
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new Column(
children: <Widget>[
new Container(
padding: new EdgeInsets.symmetric(horizontal: 5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new IconButton(
icon: new Container(
height: 50.0,
width: 50.0,
decoration: new BoxDecoration(
color: Colors.orange,
shape: BoxShape.circle,
),
child: new Center(
child: new Icon(
expandFlag ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
),
),
),
onPressed: () {
setState(() {
expandFlag = !expandFlag;
});
}),
new Text(
widget.title,
)
],
),
),
new ExpandableContainer(
expanded: expandFlag,
expandedHeight: 60.0 * 3,
child: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return StatefulListTile(
title: broadcast[widget.ind].contents[index],
second: broadcast[widget.ind].time[index],
);
},
itemCount: broadcast[widget.ind].contents.length,
))
],
),
);
}
}
class ExpandableContainer extends StatelessWidget {
final bool expanded;
final double expandedHeight;
final Widget child;
ExpandableContainer({
#required this.child,
this.expandedHeight,
this.expanded = true,
});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return new AnimatedContainer(
duration: new Duration(milliseconds: 100),
curve: Curves.easeInOut,
width: screenWidth,
height: expanded ? expandedHeight : 0.0,
child: new Container(
child: child,
decoration: new BoxDecoration(border: new Border.all(width: 1.0)),
),
);
}
}
class StatefulListTile extends StatefulWidget {
const StatefulListTile({this.title, this.second});
final String title;
final int second;
#override
_StatefulListTileState createState() => _StatefulListTileState();
}
class _StatefulListTileState extends State<StatefulListTile> {
Color _iconColor = Colors.white;
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
#override
initState() {
super.initState();
var initializationSettingsAndroid =
new AndroidInitializationSettings('#mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings();
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
}
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
border: new Border.all(width: 1.0, color: Colors.grey),
color: Colors.blue,),
child: new ListTile(
title: new Text(widget.title),
leading: new IconButton(
icon: Icon(Icons.star, color: _iconColor),
onPressed: () {
setState(() {
if (_iconColor == Colors.white) {
_iconColor = Colors.yellow;
_showNotification(widget.second);
} else {
_iconColor = Colors.white;
}
});
},
),
),
);
}
Future onSelectNotification(String payload) async {
showDialog(
context: context,
builder: (_) {
return new AlertDialog(
title: Text("PayLoad"),
content: Text("Payload : $payload"),
);
},
);
}
Future _showNotification(second) async {
var time = new Time(10, 18, second);
var androidPlatformChannelSpecifics =
new AndroidNotificationDetails('show weekly channel id',
'show weekly channel name', 'show weekly description');
var iOSPlatformChannelSpecifics =
new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
0,
widget.title,
'',
Day.Monday,
time,
platformChannelSpecifics,
payload: widget.title);
}
}
class Broadcast {
final String title;
List<String> contents;
List<int> time = [];
Broadcast(this.title, this.contents, this.time);
}
List<Broadcast> broadcast = [
new Broadcast(
'A',
['1', '2', '3'],
[5, 10, 15],
),
new Broadcast(
'B',
['4', '5'],
[20, 25],
),
];
You need to change channelID for each notification you don't want to stack.
flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(your_channelID_goes_here,
widget.title,
'',
Day.Monday,
time,
platformChannelSpecifics,
payload: widget.title);
Just change the channelID. You have put it as 0. change it is as 0,1,2,3 then you will get multiple notifications
flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime(
"change Here channelID as 0 or 1 or 2 or 3",
widget.title,
'',
Day.Monday,
time,
platformChannelSpecifics,
payload: widget.title);
File:homepage.dart
class _HomePageState extends State<HomePage> {
var _scaffoldBody;
var _scaffoldTitle;
#override
initState() {
_scaffoldTitle=new Text("Wall");
_scaffoldBody=new Center(child:CircularProgressIndicator());
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: new Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color:Theme.of(context).accentColor,
),
),
ListTile(
title: Text('Home'),
onTap: () {
setState(() {
_scaffoldTitle=new Text("Home");
_scaffoldBody=new Text("Home Page");
});
Navigator.pop(context);
},
),
ListTile(
title: Text('MenuItem1'),
onTap: () {
setState(() {
_scaffoldTitle=new Text("1st Menu");
_scaffoldBody=new TestPage("Page 1");
});
Navigator.pop(context);
},
),
ListTile(
title: Text('MenuItem2'),
onTap: () {
setState(() {
_scaffoldTitle=new Text("2nd Item");
_scaffoldBody=new TestPage("Page 2");
});
Navigator.pop(context);
},
),
],
),
),
appBar: new AppBar(
title: _scaffoldTitle,
elevation: 2.0,
actions: <Widget>[
],
),
body:_scaffoldBody,
);
}
file: TestPage.dart
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget{
final String rollNumber;
TestPage(this.rollNumber);
#override
TestPageState createState() => new TestPageState(rollNumber);
}
class TestPageState extends State<TestPage>{
String rollNumber;
TestPageState(this.rollNumber);
#override
Widget build(BuildContext context) {
return new Text(rollNumber);
}
}
Output:
When I chose : "Home" from drawer it shows "Home Page"
Then I chose : "MenuItem1" it showed "Page 1"
Then I chose : "MenuItem2" it showed same "Page 1" (unexpected)
Then I chose : "Home" it showed "Home Page"
Then I chose : "MenuItem2" it showed correctly as "Page 2"
Then I chose : "MenuItem1" it showed "Page 2" (unexpected)
unable to solve this.Stuck!! Any solutions are appreciated. Thanks in advance!!
Update your TestPart.dart as follow:
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget{
final String rollNumber;
TestPage(this.rollNumber);
#override
TestPageState createState() => new TestPageState();
}
class TestPageState extends State<TestPage>{
#override
Widget build(BuildContext context) {
return new Text(widget.rollNumber);
}
}
My StudentPage Class
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'calendar_utils.dart';
import 'dart:async';
final mainReference = FirebaseDatabase.instance.reference();
class StudentPage extends StatefulWidget{
final String rollNumber;
StudentPage(this.rollNumber);
#override
StudentPageState createState() => new StudentPageState(rollNumber);
}
class StudentPageState extends State<StudentPage>{
final String currentRoll,currentYear="2018-19";
StudentPageState(this.currentRoll);
List<String> academicMonth=["June","July","August","September","October","November","December",
"January","February","March","April","May"];
int firstHalfYear=2018,secondHalfYear=2019;
List<Widget> _monthListArray=[new ListTile(title:new Text("Academic Year",style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0),),)];
List<Widget> _listView;
int _no_of_working=0;
int _no_of_present=0;
#override
void initState() {
// TODO: implement initState
_listView=[new Center(
child: new CircularProgressIndicator(),
)];
_loadMonths();
}
#override
Widget build(BuildContext context) {
return new RefreshIndicator(child: new ListView(
children: _listView,
), onRefresh: _loadMonths);
}
Future<Null> _loadMonths() async {
_listView.clear();
await mainReference.child("XXXX").child("attendance").child(
widget.rollNumber).child(currentYear).once().then((
DataSnapshot dataSnapshot) {
try {
int monthIndex=5; //monthIndex starts from June
for(var month in academicMonth){
debugPrint("Month:"+month);
monthIndex=(monthIndex+1)%12; //month index cycles throughout 1-12
if(monthIndex==0) monthIndex=12;
List<Widget> _daysList=[];
_monthListArray.add(new Padding(padding: EdgeInsets.all(16.0),child: new Text(month+" "+(monthIndex<6?secondHalfYear:firstHalfYear).toString(),style: new TextStyle(color: Colors.black87,fontWeight: FontWeight.bold,fontSize: 20.0),),)); //initializing the month
_monthListArray.add(new Padding(padding: EdgeInsets.only(top: 10.0,bottom: 10.0),child:
new Row(children: <Widget>[
new Expanded(
child: new Center(child: new Text("S",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("M",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("T",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("W",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("T",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("F",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
new Expanded(
child: new Center(child: new Text("S",style: new TextStyle(fontWeight: FontWeight.bold),),),
),
],),));
debugPrint(monthIndex.toString());
int freeSpace=CalendarUtils(1,monthIndex,monthIndex<6?secondHalfYear:firstHalfYear).getDayFromDate();
debugPrint("FreeSpace---"+freeSpace.toString());
if(freeSpace!=0){
for (var i = 0; i < freeSpace; i++) {
_daysList.add(new Text(""));
}
}
debugPrint(monthIndex.toString());
var year=monthIndex<6?secondHalfYear:firstHalfYear;
debugPrint("Year"+year.toString());
debugPrint("Forloop limit:"+CalendarUtils(1,monthIndex,monthIndex<6?firstHalfYear:secondHalfYear).numberOfDays().toString());
for(var day=1;day<=CalendarUtils(1,monthIndex,monthIndex<6?secondHalfYear:firstHalfYear).numberOfDays();day++){
try {
//debugPrint(day.toString()+":"+dataSnapshot.value[month][day].toString());
if (CalendarUtils(day, monthIndex,
monthIndex < 6 ? secondHalfYear: firstHalfYear)
.getDayFromDate() != 0){
if (dataSnapshot.value[month][day].toString() == "1") {
_no_of_working++;
_no_of_present++;
_daysList.add(
new Padding(padding: EdgeInsets.only(left: 10.0,right: 10.0,top: 2.0,bottom: 2.0),
child: new Container(
alignment: Alignment.center,
width: 30.0,
height: 30.0,
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(50.0)),
color: Colors.green),
child: new Text(
day.toString(),
style: new TextStyle(color: Colors.white),
),
),
)
);
}
else if (dataSnapshot.value[month][day].toString() == "0"){
_no_of_working++;
_daysList.add(
new Padding(padding: EdgeInsets.only(left: 10.0,right: 10.0,top: 2.0,bottom: 2.0),
child: new Container(
alignment: Alignment.center,
width: 30.0,
height: 30.0,
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(50.0)),
color: Colors.redAccent),
child: new Text(
day.toString(),
style: new TextStyle(color: Colors.white),
),
),
)
);
}
else {
_daysList.add(
new Padding(padding: EdgeInsets.only(left: 10.0,right: 10.0,top: 2.0,bottom: 2.0),
child: new Container(
alignment: Alignment.center,
width: 30.0,
height: 30.0,
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(new Radius.circular(50.0)),
),
child: new Text(
day.toString(),
style: new TextStyle(color: Colors.black),
),
),
)
);
}
}else {
_daysList.add(
new Center(child: new Text(day.toString(), style:
new TextStyle(color: Colors.black45),)));
}
}catch (e){
_daysList.add(new Center(child:new Text(day.toString(),style:
new TextStyle(color: Colors.black),) ));
}
}
Widget _daysGrid=new GridView.count(crossAxisCount: 7,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
childAspectRatio: 1.5,
children: _daysList,
);
_monthListArray.add(_daysGrid);
}
}catch(e){}
_monthListArray.add(new Text((_no_of_present/_no_of_working).toString()));
});
this.setState((){
_listView=_monthListArray;
});
}
}
My HomePage.dart
import 'package:flutter/material.dart';
import 'package:smart_school_parent/TestPage.dart';
import 'package:smart_school_parent/attendance.dart';
import 'package:smart_school_parent/post.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:smart_school_parent/auth.dart';
import 'dart:async';
class HomePage extends StatefulWidget {
final BaseAuth auth;
final VoidCallback onSignOut;
HomePage({Key key, this.auth, this.onSignOut}) : super(key: key);
#override
_HomePageState createState() => new _HomePageState(this.auth);
}
class _HomePageState extends State<HomePage> {
final mainReference = FirebaseDatabase.instance.reference();
List<PostData> post_list = new List();
var _scaffoldBody;
var _loading;
var _currentYear;
var _scaffoldTitle;
List<Widget> _childrenList=[new Text("Profiles",textAlign: TextAlign.left,style: new TextStyle(fontWeight: FontWeight.bold),)];
BaseAuth auth;
_HomePageState(this.auth);
#override
initState() {
//_children=updateChildren();
_updateChildren();
getList();
_loading=true ;
_scaffoldTitle=new Text("Wall");
_scaffoldBody=new Center(child:CircularProgressIndicator());
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: new Drawer(
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('XXXXx'),
decoration: BoxDecoration(
color:Theme.of(context).accentColor,
),
),
ListTile(
title: Text('Wall'),
onTap: () {
setState(() {
_scaffoldTitle=new Text("Wall");
_scaffoldBody=new RefreshIndicator(child: _loadWall(), onRefresh: getList);
});
Navigator.pop(context);
},
),
Column(
children: _childrenList,
),
],
),
),
appBar: new AppBar(
title: _scaffoldTitle,
elevation: 2.0,
actions: <Widget>[
],
),
body:_scaffoldBody,
);
}
Future <Null> getList() async {
await mainReference.child("NISE-Coimbatore").child("posts").once().then((DataSnapshot dataSnapshot) {
this.setState(() {
post_list.clear();
if(dataSnapshot.value!=null){
for (var value in dataSnapshot.value.values) {
post_list.add(new PostData.fromJson(value));
//debugPrint(value.toString());
}
}else{
this.setState((){
});
}
});
});
setState(() {
_scaffoldBody=new RefreshIndicator(child: _loadWall(), onRefresh: getList);
});
}
Widget _loadWall(){
return Stack(
children: <Widget>[
new ListView.builder(itemBuilder: (BuildContext context,int index){
return new Post(post_list[index].image,post_list[index].title,post_list[index].content);
},
itemCount: post_list == null ? 0 : post_list.length,)
],
);
}
_updateChildren() async {
_currentYear= await mainReference.child("attendance").child("currentYear").once();
await auth.currentUser().then((String userId) async{
mainReference.child("NISE-Coimbatore").child("parents").child(userId).child('children').once().then((DataSnapshot dataSnapshot){
for (var value in dataSnapshot.value){
this.setState((){
_childrenList.add(
new Padding(
padding: EdgeInsets.only(left: 25.0),
child: ListTile(
title: Text(value.toString()),
onTap: () {
super.setState(() {
_scaffoldTitle=new Text(value.toString());
this._scaffoldBody=new StudentPage(value.toString());
});
Navigator.pop(context);
},
),
),
);
});
//debugPrint("Childrrncount::"+value.toString());
}
});
});
}
}
class PostData{
String title;
String content;
String image;
PostData(this.title, this.content, this.image);
PostData.fromJson(var value) {
this.title = value['title'];
this.content = value['content'];
this.image = value['image'];
}
}
_childrenList has two elements. The datasnapshot.values has [15505,15501] two roll numbers
Both rollnumbers from firebase appear on the drawer box and on tap it is expected to show StudentPage(15501.tostring()) or StudentPage(15505.tostring())
it works as expected if select "Wall" from drawer and then any of the "rollNumbers"
it is not working if i switch from one of the rollNumbers to other rollNumber in the drawer.
Similar to my first post. Only the _scaffoldTitle changes accordingly.
i want to achieve this design of dropdown but not sure how to get this type of dropdown i have tried doing this
Container(
padding: EdgeInsets.all(10.0),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
items:_locations.map((String val){
return DropdownMenuItem<String>(
value: val,
child: Container(
margin: EdgeInsets.only(left: 10.0,right: 10.0),
child: new Text(val)
),
);
}).toList(),
hint:Text(_SelectdType),
onChanged:(String val){
_SelectdType = val;
setState(() {});
}
),
),
)
The hint for the DropdownButton and the child for the DropdownMenuItem are just widgets like everything else, so you can put whatever you want in there.
This example uses a Map to supply the text and icons:
final Map<String, IconData> _data = Map.fromIterables(
['First', 'Second', 'Third'],
[Icons.filter_1, Icons.filter_2, Icons.filter_3]);
String _selectedType;
IconData _selectedIcon;
...
Container(
padding: EdgeInsets.all(10.0),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
items: _data.keys.map((String val) {
return DropdownMenuItem<String>(
value: val,
child: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Icon(_data[val]),
),
Text(val),
],
),
);
}).toList(),
hint: Row(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.0),
child:
Icon(_selectedIcon ?? _data.values.toList()[0]),
),
Text(_selectedType ?? _data.keys.toList()[0]),
],
),
onChanged: (String val) {
setState(() {
_selectedType = val;
_selectedIcon = _data[val];
});
}),
),
),
You can use a Row to build your DropdownMenuItem.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.redAccent,
),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Row(children: [
IconButton(
icon: Icon(Icons.home),
color: Colors.redAccent,
),
Text(value),
]),
);
}).toList(),
);
}
}
You can use it like:
import 'package:flutter/material.dart';
class SettingsWidget extends StatefulWidget {
SettingsWidget({Key key}) : super(key: key);
#override
_SettingsWidgetState createState() => new _SettingsWidgetState();
}
class _SettingsWidgetState extends State<SettingsWidget> {
List _cities =
["Cluj-Napoca", "Bucuresti", "Timisoara", "Brasov", "Constanta"];
List<DropdownMenuItem<String>> _dropDownMenuItems;
String _currentCity;
#override
void initState() {
_dropDownMenuItems = getDropDownMenuItems();
_currentCity = _dropDownMenuItems[0].value;
super.initState();
}
List<DropdownMenuItem<String>> getDropDownMenuItems() {
List<DropdownMenuItem<String>> items = new List();
for (String city in _cities) {
items.add(new DropdownMenuItem(
value: city,
child: new Text(city)
));
}
return items;
}
#override
Widget build(BuildContext context) {
return new Container(
color: Colors.white,
child: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text("Please choose your city: "),
new Container(
padding: new EdgeInsets.all(16.0),
),
new DropdownButton(
value: _currentCity,
items: _dropDownMenuItems,
onChanged: changedDropDownItem,
)
],
)
),
);
}
void changedDropDownItem(String selectedCity) {
setState(() {
_currentCity = selectedCity;
});
}
}
The animation of ListView items that is about dragging to the left and the delete button appears works fine. The problem is that if I leave the delete button appearing and changing the page, when I return pop() to the previous page the button keeps appearing.
The animation did not go back to the beginning. The same happens if I update the items in the ListView, for example deleting an item. The item is deleted but the animation is not.
It looks like I'm updating the ListView content and not the ListView itself.
How could you solve this problem?
I'm trying to destroy ListView with every update in its items, but I'm not getting it. I also do not know if this is the right way to solve the problem.
The ListView is built through the buildTile() function and its items come from the database.
Below I put the code and a gif demonstrating what the problem is.
To work the code below, you need to insert the sqflite and path_provider dependencies into pubspec.yaml, thus:
dependencies:
sqflite: any
path_provider: any
flutter:
sdk: flutter
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
enum DialogOptionsAction {
cancel,
ok
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
routes: <String, WidgetBuilder> {
'/newpage': (BuildContext context) => new NewPage(),
},
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseClient _db = new DatabaseClient();
List listCategory = [];
List<Widget> tiles;
List colors = [
const Color(0xFFFFA500),
const Color(0xFF279605),
const Color(0xFF005959)
];
createdb() async {
await _db.create().then(
(data){
_db.getAllCategory().then((list){
setState(() {
this.listCategory = list;
});
});
}
);
}
#override
void initState() {
super.initState();
createdb();
}
void showCategoryDelete<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
)
.then<Null>((T value) {
if (value != null) {
setState(() { print(value); });
}
});
}
#override
Widget build(BuildContext context) {
List<Widget> buildTile(List list) {
this.tiles = [];
for(var dict in list) {
this.tiles.add(
new ItemCategory(
id: dict['id'],
category: dict['name'],
color: this.colors[dict['color']],
onPressed: () async {
showCategoryDelete<DialogOptionsAction>(
context: context,
child: new AlertDialog(
title: const Text('Delete Category'),
content: new Text(
'Do you want to delete this category?',
style: new TextStyle(
color: Colors.black26,
fontSize: 16.0,
fontFamily: "Roboto",
fontWeight: FontWeight.w500,
)
),
actions: <Widget>[
new FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
}
),
new FlatButton(
child: const Text('OK'),
onPressed: () {
_db.deleteCategory(dict['id']).then(
(list) {
setState(() {
this.listCategory = list;
});
}
);
Navigator.pop(context);
}
)
]
)
);
},
)
);
}
return this.tiles;
}
return new Scaffold(
appBar: new AppBar(
title: new Text('Categories'),
actions: <Widget>[
new IconButton(
icon: const Icon(Icons.add),
color: new Color(0xFFFFFFFF),
onPressed: () async {
await Navigator.of(context).pushNamed('/newpage').then(
(data){
_db.getAllCategory().then((list){
setState(() {
this.listCategory = list;
});
});
}
);
}
)
],
),
body: new ListView(
padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
children: buildTile(this.listCategory)
)
);
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('New Page'),
),
);
}
}
//Creating Database with some data and two queries
class DatabaseClient {
Database db;
Future create() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
db = await openDatabase(dbPath, version: 1, onCreate: this._create);
}
Future _create(Database db, int version) async {
await db.execute("""
CREATE TABLE category (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
color INTEGER NOT NULL
)""");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
}
Future getAllCategory() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
List list = await db.rawQuery('SELECT * FROM category');
await db.close();
return list;
}
Future deleteCategory(int id) async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
await db.delete('category', where: "id = ?", whereArgs: [id]);
List list = await db.rawQuery('SELECT * FROM category');
await db.close();
return list;
}
}
//Creating ListViews items
class ItemCategory extends StatefulWidget {
ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);
final int id;
final String category;
final Color color;
final VoidCallback onPressed;
#override
ItemCategoryState createState() => new ItemCategoryState();
}
class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
ItemCategoryState();
DatabaseClient db = new DatabaseClient();
AnimationController _controller;
Animation<double> _animation;
double flingOpening;
bool startFling = true;
void initState() {
super.initState();
_controller = new AnimationController(duration:
const Duration(milliseconds: 246), vsync: this);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
}
void _move(DragUpdateDetails details) {
final double delta = details.primaryDelta / 304;
_controller.value -= delta;
}
void _settle(DragEndDetails details) {
if(this.startFling) {
_controller.fling(velocity: 1.0);
this.startFling = false;
} else if(!this.startFling){
_controller.fling(velocity: -1.0);
this.startFling = true;
}
}
#override
Widget build(BuildContext context) {
final ui.Size logicalSize = MediaQuery.of(context).size;
final double _width = logicalSize.width;
this.flingOpening = -(48.0/_width);
return new GestureDetector(
onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _settle,
child: new Stack(
children: <Widget>[
new Positioned.fill(
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
color: new Color(0xFFE57373),
),
child: new IconButton(
icon: new Icon(Icons.delete),
color: new Color(0xFFFFFFFF),
onPressed: widget.onPressed
)
),
],
),
),
new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: new Offset(this.flingOpening, 0.0),
).animate(_animation),
child: new Container(
decoration: new BoxDecoration(
border: new Border(
top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
),
color: new Color(0xFFFFFFFF),
),
margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Container(
margin: new EdgeInsets.only(left: 16.0),
padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Icon(
Icons.brightness_1,
color: widget.color,
size: 35.0,
),
),
new Text(
widget.category,
style: new TextStyle(
color: Colors.black87,
fontSize: 14.0,
fontFamily: "Roboto",
fontWeight: FontWeight.w500,
),
),
],
)
)
],
),
)
],
),
)
),
],
)
);
}
}
According to the Flutter - Widget animation status remains even after it has been removed
Simply insert the key into the new ItemCategory (
I needed to pass a key to the children. Or else the renderer won't be able to know which SlideTransition got removed, and use the index.
...
#override
Widget build(BuildContext context) {
List<Widget> buildTile(List list) {
this.tiles = [];
for(var dict in list) {
this.tiles.add(
new ItemCategory(
key: new Key(dict), //new
id: dict['id'],
category: dict['name'],
...
The complete code is:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
enum DialogOptionsAction {
cancel,
ok
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
routes: <String, WidgetBuilder> {
'/newpage': (BuildContext context) => new NewPage(),
},
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseClient _db = new DatabaseClient();
List listCategory = [];
List<Widget> tiles;
List colors = [
const Color(0xFFFFA500),
const Color(0xFF279605),
const Color(0xFF005959)
];
createdb() async {
await _db.create().then(
(data){
_db.getAllCategory().then((list){
setState(() {
this.listCategory = list;
});
});
}
);
}
#override
void initState() {
super.initState();
createdb();
}
void showCategoryDelete<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
)
.then<Null>((T value) {
if (value != null) {
setState(() { print(value); });
}
});
}
#override
Widget build(BuildContext context) {
List<Widget> buildTile(List list) {
this.tiles = [];
for(var dict in list) {
this.tiles.add(
new ItemCategory(
key: new Key(dict), //new
id: dict['id'],
category: dict['name'],
color: this.colors[dict['color']],
onPressed: () async {
showCategoryDelete<DialogOptionsAction>(
context: context,
child: new AlertDialog(
title: const Text('Delete Category'),
content: new Text(
'Do you want to delete this category?',
style: new TextStyle(
color: Colors.black26,
fontSize: 16.0,
fontFamily: "Roboto",
fontWeight: FontWeight.w500,
)
),
actions: <Widget>[
new FlatButton(
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
}
),
new FlatButton(
child: const Text('OK'),
onPressed: () {
_db.deleteCategory(dict['id']).then(
(list) {
setState(() {
this.listCategory = list;
});
}
);
Navigator.pop(context);
}
)
]
)
);
},
)
);
}
return this.tiles;
}
return new Scaffold(
appBar: new AppBar(
title: new Text('Categories'),
actions: <Widget>[
new IconButton(
icon: const Icon(Icons.add),
color: new Color(0xFFFFFFFF),
onPressed: () async {
await Navigator.of(context).pushNamed('/newpage').then(
(data){
_db.getAllCategory().then((list){
setState(() {
this.listCategory = list;
});
});
}
);
}
)
],
),
body: new ListView(
padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0),
children: buildTile(this.listCategory)
)
);
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('New Page'),
),
);
}
}
//Creating Database with some data and two queries
class DatabaseClient {
Database db;
Future create() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
db = await openDatabase(dbPath, version: 1, onCreate: this._create);
}
Future _create(Database db, int version) async {
await db.execute("""
CREATE TABLE category (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
color INTEGER NOT NULL
)""");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)");
await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)");
}
Future getAllCategory() async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
List list = await db.rawQuery('SELECT * FROM category');
await db.close();
return list;
}
Future deleteCategory(int id) async {
Directory path = await getApplicationDocumentsDirectory();
String dbPath = join(path.path, "database.db");
Database db = await openDatabase(dbPath);
await db.delete('category', where: "id = ?", whereArgs: [id]);
List list = await db.rawQuery('SELECT * FROM category');
await db.close();
return list;
}
}
//Creating ListViews items
class ItemCategory extends StatefulWidget {
ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key);
final int id;
final String category;
final Color color;
final VoidCallback onPressed;
#override
ItemCategoryState createState() => new ItemCategoryState();
}
class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin {
ItemCategoryState();
DatabaseClient db = new DatabaseClient();
AnimationController _controller;
Animation<double> _animation;
double flingOpening;
bool startFling = true;
void initState() {
super.initState();
_controller = new AnimationController(duration:
const Duration(milliseconds: 246), vsync: this);
_animation = new CurvedAnimation(
parent: _controller,
curve: new Interval(0.0, 1.0, curve: Curves.linear),
);
}
void _move(DragUpdateDetails details) {
final double delta = details.primaryDelta / 304;
_controller.value -= delta;
}
void _settle(DragEndDetails details) {
if(this.startFling) {
_controller.fling(velocity: 1.0);
this.startFling = false;
} else if(!this.startFling){
_controller.fling(velocity: -1.0);
this.startFling = true;
}
}
#override
Widget build(BuildContext context) {
final ui.Size logicalSize = MediaQuery.of(context).size;
final double _width = logicalSize.width;
this.flingOpening = -(48.0/_width);
return new GestureDetector(
onHorizontalDragUpdate: _move,
onHorizontalDragEnd: _settle,
child: new Stack(
children: <Widget>[
new Positioned.fill(
child: new Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Container(
decoration: new BoxDecoration(
color: new Color(0xFFE57373),
),
child: new IconButton(
icon: new Icon(Icons.delete),
color: new Color(0xFFFFFFFF),
onPressed: widget.onPressed
)
),
],
),
),
new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: new Offset(this.flingOpening, 0.0),
).animate(_animation),
child: new Container(
decoration: new BoxDecoration(
border: new Border(
top: new BorderSide(style: BorderStyle.solid, color: Colors.black26),
),
color: new Color(0xFFFFFFFF),
),
margin: new EdgeInsets.only(top: 0.0, bottom: 0.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Expanded(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Container(
margin: new EdgeInsets.only(left: 16.0),
padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5),
child: new Row(
children: <Widget>[
new Container(
margin: new EdgeInsets.only(right: 16.0),
child: new Icon(
Icons.brightness_1,
color: widget.color,
size: 35.0,
),
),
new Text(
widget.category,
style: new TextStyle(
color: Colors.black87,
fontSize: 14.0,
fontFamily: "Roboto",
fontWeight: FontWeight.w500,
),
),
],
)
)
],
),
)
],
),
)
),
],
)
);
}
}