Related
I am currently displaying my icons like this:
Widget _buildPopupDialog(BuildContext context) {
List<IconData> _iconsTable = [
Icons.feedback,
Icons.eco,
Icons.support,
Icons.call,
Icons.nature_people,
Icons.directions_bike,
];
return new AlertDialog(
content: SingleChildScrollView(
child: new Container(
child: GridView.count(
children: new List.generate(6, (int index) {
return new Positioned(
child: new DailyButton(iconData: _iconsTable[index]),
);
}),
),
),
),
However, I am wanting to get the icon data from cloud firestore. I am very new to using both flutter and firebase so I am very unsure how I would be able to do this. So far, I have tried this but iconData: Icons.iconsData obviously doesnt work:
class MyApp3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage3(),
);
}
}
class MyHomePage3 extends StatefulWidget {
#override
_MyHomePageState3 createState() {
return _MyHomePageState3();
}
}
class _MyHomePageState3 extends State<MyHomePage3> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: _buildBody(context),
);
}
Widget _buildBody(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance.collection('icons').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return _buildList(context, snapshot.data.docs);
},
);
}
Widget _buildList(BuildContext context, List<DocumentSnapshot> snapshot) {
return ListView(
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
);
}
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
var _iconsData = record3.name;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: Card(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: Icons.iconsData,
),
// trailing: Text(record3.votes.toString()),
// onTap: () => record3.reference.update({'votes': record3.votes+1})
),
),
);
}
}
class Record3 {
final String name;
final int votes;
final DocumentReference reference;
Record3.fromMap(Map<String, dynamic> map, {this.reference})
: assert(map['name'] != null),
assert(map['votes'] != null),
name = map['name'],
votes = map['votes'];
Record3.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data(), reference: snapshot.reference);
#override
String toString() => "Record<$name:$votes>";
}
Any help would be greatly appreciated!
If anyone is interested, I was able to figure it out:
Widget _buildListItem(BuildContext context, DocumentSnapshot data) {
final record3 = Record3.fromSnapshot(data);
int iconCode = record3.votes;
return Padding(
key: ValueKey(record3.name),
child: Container(
child: new Container(
child: new ListView(
scrollDirection: Axis.horizontal,
children: new List.generate(1, (int index) {
return new Positioned(
child: new MoodButton(
onTap: () => print("Mood"),
iconData: (IconData(iconCode, fontFamily: 'MaterialIcons')),
),
);
})),
),
),
);
I need to detect TabBar when I swipe then print somethings on console, how I can do that? This is my code.
bottomNavigationBar: new Material(
color: Colors.blueAccent,
child: new TabBar(
onTap: (int index){ setState(() {
_onTap(index);
});},
indicatorColor: Colors.white,
controller: controller,
tabs: <Widget>[
new Tab(icon: new Icon(Icons.shopping_basket)),
new Tab(icon: new Icon(Icons.store)),
new Tab(icon: new Icon(Icons.local_offer)),
new Tab(icon: new Icon(Icons.assignment)),
new Tab(icon: new Icon(Icons.settings)),
],
)
),
You need to add a listener to your tab controller - maybe in initState.
controller.addListener((){
print('my index is'+ controller.index.toString());
});
Swipe functionality is not provided by onTap() function, for that TabController is used
class TabBarDemo extends StatefulWidget {
#override
_TabBarDemoState createState() => _TabBarDemoState();
}
class _TabBarDemoState extends State<TabBarDemo>
with SingleTickerProviderStateMixin {
TabController _controller;
int _selectedIndex = 0;
List<Widget> list = [
Tab(icon: Icon(Icons.card_travel)),
Tab(icon: Icon(Icons.add_shopping_cart)),
];
#override
void initState() {
// TODO: implement initState
super.initState();
// Create TabController for getting the index of current tab
_controller = TabController(length: list.length, vsync: this);
_controller.addListener(() {
setState(() {
_selectedIndex = _controller.index;
});
print("Selected Index: " + _controller.index.toString());
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (index) {
// Should not used it as it only called when tab options are clicked,
// not when user swapped
},
controller: _controller,
tabs: list,
),
title: Text('Tabs Demo'),
),
body: TabBarView(
controller: _controller,
children: [
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
],
),
),
);
}
}
Github Repo:
https://github.com/jitsm555/Flutter-Problems/tree/master/tab_bar_tricks
Output:
Here is a full example. Use a TabController and add a callback using addListener.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: MyTabbedPage(),
);
}
}
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {
var _context;
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
_tabController.addListener(_handleTabSelection);
}
void _handleTabSelection() {
if (_tabController.indexIsChanging) {
switch (_tabController.index) {
case 0:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 1 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
case 1:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 2 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
}
}
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: Builder(
builder: (context) {
_context = context;
return TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
final String label = tab.text.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
);
},
),
);
}
}
You can create wrapper widget
class _DefaultTabControllerListener extends StatefulWidget {
const _DefaultTabControllerListener(
{Key? key, this.onTabSelected, required this.child})
: super(key: key);
final void Function(int index)? onTabSelected;
final Widget child;
#override
_DefaultTabControllerListenerState createState() =>
_DefaultTabControllerListenerState();
}
class _DefaultTabControllerListenerState
extends State<_DefaultTabControllerListener> {
late final void Function()? _listener;
TabController? _tabController;
#override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
final tabController = DefaultTabController.of(context)!;
_listener = () {
final onTabSelected = widget.onTabSelected;
if (onTabSelected != null) {
onTabSelected(tabController.index);
}
};
tabController.addListener(_listener!);
});
}
#override
void didChangeDependencies() {
_tabController = DefaultTabController.of(context);
super.didChangeDependencies();
}
#override
void dispose() {
if (_listener != null && _tabController != null) {
_tabController!.removeListener(_listener!);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
And wrap TabBar with this widget
DefaultTabController(
child: _DefaultTabControllerListener(
onTabSelected: (index) {
// Handler
},
child: TabBar(.....
We can listen to tab scroll notification by using NotificationListener Widget
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
_onStartScroll(scrollNotification.metrics);
}
},
child: _buildTabBarAndTabBarViews(),
);
}
_onStartScroll(ScrollMetrics metrics) {
print('hello world');
}
}
I ran into a similar issue and following is what I did to accomplish the same: -
import 'package:flutter/material.dart';
import '../widgets/basic_dialog.dart';
import 'sign_in_form.dart';
import 'sign_up_form.dart';
class LoginDialog extends StatefulWidget {
#override
_LoginDialogState createState() => _LoginDialogState();
}
class _LoginDialogState extends State<LoginDialog>
with SingleTickerProviderStateMixin {
int activeTab = 0;
TabController controller;
#override
void initState() {
super.initState();
controller = TabController(vsync: this, length: 2);
}
#override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification) {
setState(() {
if (notification.metrics.pixels <= 100) {
controller.index = 0;
} else {
controller.index = 1;
}
});
return true;
},
child: BasicDialog(
child: Container(
height: controller.index == 0
? MediaQuery.of(context).size.height / 2.7
: MediaQuery.of(context).size.height / 1.8,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TabBar(
controller: controller,
tabs: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text('Sign In'),
),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text('Sign up'),
),
],
),
Expanded(
child: TabBarView(
controller: controller,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: SignInForm(),
),
),
// If a container is not displayed during the tab switch to tab0, renderflex error is thrown because of the height change.
controller.index == 0
? Container()
: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: SignUpForm(),
),
),
],
),
)
],
),
),
),
);
}
}
If you are using DefaultTabController and want to listen to updates in TabBar, you can expose the controller using the DefaultTabController.of method and then add a listener to it:
DefaultTabController(
length: 3,
child: Builder(
builder: (BuildContext context) {
final TabController controller = DefaultTabController.of(context)!;
controller.addListener(() {
if (!controller.indexIsChanging) {
print(controller.index);
// add code to be executed on TabBar change
}
});
return Scaffold(...
Here you have a full example:
class TabControllerDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Builder(builder: (BuildContext context) {
final TabController controller = DefaultTabController.of(context)!;
controller.addListener(() {
if (!controller.indexIsChanging) {
print(controller.index);
// add code to be executed on TabBar change
}
});
return Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(text: "Tab 0"),
Tab(text: "Tab 1"),
Tab(text: "Tab 2"),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Center(child: Text('View 0')),
Center(child: Text('View 1')),
Center(child: Text('View 2')),
],
),
);
})),
);
}
}
You can also check this DartPad LiveDemo.
Warp your tab in BottomNavigationBar . it will give you option onTap() where you can check which tab will clicked.
using this code you will also redirect to particular page when you tap on Tab
import 'package:flutter/material.dart';
class BottomBarList extends StatefulWidget {
#override
_BottomBarListState createState() => _BottomBarListState();
}
class _BottomBarListState extends State<BottomBarList> {
int bottomSelectedIndex = 0;
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
AllMovieList(),
MovieDescription(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(),
bottomNavigationBar: bottomBar(),
body:_widgetOptions.elementAt(_selectedIndex),
);
}
bottomBar() {
return BottomNavigationBar(
type: BottomNavigationBarType.shifting,
unselectedItemColor: Colors.grey,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.tv),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
backgroundColor: Colors.orange,
onTap: _onItemTapped,
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
You can use the only scrollNotification (ScrollEndNotification) of the NotificationListener. It covers the tap and swipe actions.
class HandlingTabChanges extends State<JsonTestDetailFrame> with SingleTickerProviderStateMixin {
late final TabController _tabController;
final int _tabLength = 2;
#override
void initState() {
super.initState();
_tabController = TabController(length: _tabLength, vsync: this);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabLength,
child: Scaffold(
appBar: AppBar(
title: Text("Some title"),
bottom: TabBar(
controller: _tabController,
tabs: [
Icon(Icons.settings),
Icon(Icons.list_alt),
],
),
),
body: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) _onTabChanged();
return false;
},
child: TabBarView(
controller: _tabController,
children: [
SomeWidget1(),
SomeWidget2(),
],
),
),
),
);
}
void _onTabChanged() {
switch (_tabController.index) {
case 0:
// handle 0 position
break;
case 1:
// handle 1 position
break;
}
}
}
You can disable swiping effect on TabBarView by adding:
physics: NeverScrollableScrollPhysics(),
and declaring one TabController and assigning that to your TabBar and TabBarView:
TabController _tabController;
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 get the list of files from the user's folder. The names of the files I transfer to the ListView.builder. It's work, but I think, this is bad architecture.
A method _getFilesFromDir() call with a high frequency.
How to make the correct list generation, so as not to update the interface without changing the file list?
class CharacteristList extends StatefulWidget {
#override
_CharacteristListState createState() => new _CharacteristListState();
}
class _CharacteristListState extends State<CharacteristList> {
List<String> filesList = new List<String>();
List<String> filesL = new List<String>();
#override
void initState() {
super.initState();
filesList = [];
}
Future<List<String>> _getFilesFromDir() async{
filesL = await FilesInDirectory().getFilesFromDir();
setState(() {
filesList = filesL;
});
return filesList;
}
_getFilesCount(){
_getFilesFromDir();
int count = filesList.length;
return count;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Список документов'),
),
body: new Column(
children: <Widget>[
new Expanded(
child: new ListView.builder(
//TODO не успевает сформировать список файлов
itemCount: _getFilesCount(),
itemBuilder: (context, index){
return new CharacteristListItem(filesList[index]);
},
),
),
],
),
floatingActionButton: new FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context)
=> new StartScreen()),
);},
child: new Icon(Icons.add),
),
);
}
}
// add dependancy in pubspec.yaml
path_provider:
import 'dart:io' as io;
import 'package:path_provider/path_provider.dart';
//Declare Globaly
String directory;
List file = new List();
#override
void initState() {
// TODO: implement initState
super.initState();
_listofFiles();
}
// Make New Function
void _listofFiles() async {
directory = (await getApplicationDocumentsDirectory()).path;
setState(() {
file = io.Directory("$directory/resume/").listSync(); //use your folder name insted of resume.
});
}
// Build Part
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'List of Files',
home: Scaffold(
appBar: AppBar(
title: Text("Get List of Files with whole Path"),
),
body: Container(
child: Column(
children: <Widget>[
// your Content if there
Expanded(
child: ListView.builder(
itemCount: file.length,
itemBuilder: (BuildContext context, int index) {
return Text(file[index].toString());
}),
)
],
),
),
),
);
}
Don't call _getFilesCount() in build(). build() can be called very frequently. Call it in initState() and store the result instead of re-reading over and over again.
I changed the architecture of the class - I used FutureBuilder.
class _CharacteristListState extends State<CharacteristList> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text('Список документов'),
),
body: new Center(
child: new Column(
children: <Widget>[
new FutureBuilder(
future: _inFutureList(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.connectionState == ConnectionState.waiting){
return new Text('Data is loading...');
}
else{
return customBuild(context, snapshot);
}
}
)
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: () {
Navigator.push(context,
new MaterialPageRoute(builder: (context)
=> new StartScreen()),
);},
child: new Icon(Icons.add),
),
);
}
Widget customBuild(BuildContext context, AsyncSnapshot snapshot){
List<String> values = snapshot.data;
return new Container(
child: new Expanded(
child: new ListView.builder(
itemCount: values.length,
itemBuilder: (context, index){
return new CharacteristListItem(values[index]);
},
),
)
);
}
Future<List<String>>_inFutureList() async{
var filesList = new List<String>();
filesList = await FilesInDirectory().getFilesFromDir();
await new Future.delayed(new Duration(milliseconds: 500));
return filesList;
}
}
// add dependancy in pubspec.yaml
path_provider:
import 'dart:io' as io;
import 'package:path_provider/path_provider.dart';
//Declare Globaly
String directory;
List file = new List();
#override
void initState() {
// TODO: implement initState
super.initState();
_listofFiles();
}
// Make New Function
void _listofFiles() async {
directory = "/storage/emulated/0/Android/data/"; //Give your folder path
setState(() {
file = io.Directory("$directory/resume/").listSync(); //use your folder name insted of resume.
});
}
// Build Part
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'List of Files',
home: Scaffold(
appBar: AppBar(
title: Text("Get List of Files with whole Path"),
),
body: Container(
child: Column(
children: <Widget>[
// your Content if there
Expanded(
child: ListView.builder(
itemCount: file.length,
itemBuilder: (BuildContext context, int index) {
return Text(file[index].toString());
}),
)
],
),
),
),
);
}
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,
),
),
],
)
)
],
),
)
],
),
)
),
],
)
);
}
}