Getting Error while Home widget (MyHomepage) loading - flutter

I am getting The following error while Run my app default page (Homepage) .
════════ Exception caught by widgets library ═══════════
The following ArgumentError was thrown building MyHomePage(dirty, dependencies: [MediaQuery, _EffectiveTickerMode], state: _MyHomePageState#7da5f(ticker inactive)):
Invalid argument(s)
**The Tracker showing the following reasons:**
The relevant error-causing widget was:
MyHomePage file:///F:/Orangebd/app/GoogleDriveClone-Flutter/lib/Screen/Home.dart:37:15
When the exception was thrown, this was the stack:
#0 _StringBase.+ (dart:core-patch/string_patch.dart:272:57)
#1 _MyHomePageState.build (package:googledriveclone_flutter/Screen/Home.dart:133:45)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4716:27)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4599:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4772:11)
...
══════════════════════════════════════════
During that error, the screen appears something like that
Here is my Home page code
import 'package:fab_circular_menu/fab_circular_menu.dart';
//import 'package:file_picker/file_picker.dart';
import 'package:floating_action_bubble/floating_action_bubble.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:googledriveclone_flutter/Screen/Files.dart';
import 'package:googledriveclone_flutter/Screen/HomeScreen.dart';
import 'package:googledriveclone_flutter/Screen/LoginPage.dart';
import 'package:googledriveclone_flutter/Screen/Profile.dart';
import 'package:googledriveclone_flutter/Widget/constants.dart';
import 'package:prompt_dialog/prompt_dialog.dart';
import 'package:sk_alert_dialog/sk_alert_dialog.dart';
import 'package:storage_capacity/storage_capacity.dart';
import 'IssudFile.dart';
void main() {
runApp(HomePage());
}
class HomePage extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
try {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Digilocker',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Digilocker'),
);
}
catch(e){
print('Loading expception of page'+e.toString());
}
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
Widget _widgetBody = HomeScreen();
int _currrentIndex = 0;
Animation<double> _animation;
AnimationController _animationController;
TextEditingController _foldername = TextEditingController();
String _fileName;
var scaffoldKey = GlobalKey<ScaffoldState>();
bool isFolder;
double _diskSpace = 0;
var _freespace ;
var _freespacemb;
var _occupiedSpace ;
var _totalSpace;
#override
void initState() {
// TODO: implement initState
// _controller.addListener(() => _extension = _controller.text);
_getStorgeInfo();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);
final curvedAnimation = CurvedAnimation(curve: Curves.easeInOut, parent: _animationController);
_animation = Tween<double>(begin: 0, end: 1).animate(curvedAnimation);
// initDiskSpace();
super.initState();
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _onItemTapped(int index) async{
setState(() {
if(index == 0){
_currrentIndex = index;
_widgetBody = HomeScreen();
}
else if(index == 1){
_currrentIndex = index;
_widgetBody = MyIssuedDocScreen();
}
else if(index == 2){
_currrentIndex = index;
_widgetBody = Center(child: Text('Shared documents'),);
}
else if(index == 3){
_currrentIndex = index;
_widgetBody = MyDriveScreen();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This way it will not open
// endDrawer: Drawer(),
drawer: new Drawer(
elevation: 10,
child: new ListView(
padding: EdgeInsets.all(0),
children: <Widget>[
DrawerHeader(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset('assets/digi_locker.png', width: MediaQuery.of(context).size.width*0.30,),
SizedBox(height: 10,),
Text('Available space: '+_freespace+'\t (MB)'),
]
),
decoration: BoxDecoration(
color: kPrimaryLightColor,
),
),
ListTile(
leading: Icon(Icons.person),
title: Text('My profile'),
onTap: () {
// Get.back();
Get.to(profilePage());
},
),
Divider(),
ListTile(
leading: Icon(Icons.create_new_folder),
title: Text('Create folder'),
onTap: () {
// Get.back();
_showMyDialog();
},
),
ListTile(
leading: Icon(Icons.cloud_upload_rounded),
title: Text('File upload'),
onTap: () {
// Get.back();
},
),
ListTile(
leading: Icon(Icons.six_ft_apart_outlined),
title: Text('Issued documents'),
onTap: () {
// Get.back();
},
),
Divider(),
ListTile(
leading: Icon(Icons.translate_rounded),
title: Text('Change lagnuage'),
onTap: () {
// Get.back();
//Get.offAll(LoginPage());
//Do some stuff here
//Closing programmatically - very less practical use
scaffoldKey.currentState.openEndDrawer();
},
),
ListTile(
leading: Icon(Icons.logout),
title: Text('Logout'),
onTap: () {
// Get.back();
Get.offAll(LoginPage());
//Do some stuff here
//Closing programmatically - very less practical use
scaffoldKey.currentState.openEndDrawer();
},
)
],
),
),
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
brightness: Theme.of(context).brightness,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children:
[
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(35)),
color: Colors.grey.shade50,
),
child: TextFormField(
decoration: InputDecoration(
hintText: "Search in locker",
border: InputBorder.none,
icon: Container(
margin: EdgeInsets.only(left: 10),
child: Icon(Icons.search, color: kPrimaryColor,)
),
),
),
),
),
]
),
iconTheme: IconThemeData(color: kPrimaryColor),
actions: <Widget>[
IconButton(
onPressed: (){
print("Sync started");
showSnackMessage(context,"Sync Started please wait...", scaffoldKey,'');
},
icon: Icon(
Icons.sync,
color:kPrimaryColor,
),
),
IconButton(
icon: Container(
height: 50,
width: 50,
margin: EdgeInsets.all(5),
child: CircleAvatar(
radius: 14.0,
backgroundColor: Colors.white,
child: CircleAvatar(
radius: 14.0,
backgroundColor: Colors.grey[200],
backgroundImage: NetworkImage("https://qph.fs.quoracdn.net/main-qimg-11ef692748351829b4629683eff21100.webp"),
),
),
),
onPressed: () {
// do something
},
)
],
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(15.0),
child: _widgetBody
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
//Init Floating Action Bubble
floatingActionButton: FloatingActionBubble(
// Menu items
items: <Bubble>[
// Floating action menu item
Bubble(
title:"Upload",
iconColor :kPrimaryColor,
bubbleColor : Colors.white.withOpacity(0.9),
titleStyle:TextStyle(fontSize: 16 , color: kPrimaryColor),
icon:Icons.cloud_upload,
onPress: () {
// OpenFilePicker();
_animationController.reverse();
_openFileType(context);
},
),
// Floating action menu item
Bubble(
title:"Folder",
icon:Icons.create_new_folder,
iconColor :kPrimaryColor,
bubbleColor : Colors.white.withOpacity(0.9),
titleStyle:TextStyle(fontSize: 16 , color: kPrimaryColor),
onPress: () {
_animationController.reverse();
print('creating folder');
_showMyDialog();
},
),
//Floating action menu item
],
// animation controller
animation: _animation,
// On pressed change animation state
onPress: _animationController.isCompleted
? _animationController.reverse
: _animationController.forward,
// Floating Action button Icon color
iconColor: kPrimaryColor,
// Flaoting Action button Icon
icon: AnimatedIcons.menu_close,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currrentIndex,
type: BottomNavigationBarType.fixed,
showSelectedLabels: true,
showUnselectedLabels: true,
selectedItemColor: kPrimaryColor,
onTap: _onItemTapped,
items: [
BottomNavigationBarItem(
icon: _currrentIndex==0?Icon(Icons.home,size: 25,):Icon(Icons.home_outlined,size: 25),
title: Text("Home")
),
BottomNavigationBarItem(
icon: _currrentIndex==1?Icon(Icons.file_download_done,size: 25,):Icon(Icons.file_download_done_outlined,size: 25),
title: Text("Issued")
),
BottomNavigationBarItem(
icon: _currrentIndex==2?Icon(Icons.supervised_user_circle,size: 25,):Icon(Icons.supervised_user_circle,size: 25),
title: Text("Shared")
),
BottomNavigationBarItem(
icon: _currrentIndex==3?Icon(Icons.folder,size: 25,):Icon(Icons.folder_open,size: 25),
title: Text("My locker")
),
],
), );
}
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
elevation: 13,
title: Text('Create folder'),
content: TextField(
onChanged: (value) { },
controller: _foldername,
decoration: InputDecoration(hintText: "your folder/directory name",
suffixIcon: IconButton(
onPressed: () => _foldername.clear(),
icon: Icon(Icons.clear),
),
),
),
actions: <Widget>[
TextButton(
child: Text('Cancel', style: TextStyle(color: Colors.red),),
onPressed: () {
//Navigator.pop(_);
Navigator.of(context).pop();
// _animationController.reverse();
},
),
TextButton(
child: Text('Create', style: TextStyle(color: kPrimaryColor),),
onPressed: () {
createFolder(context, scaffoldKey, _foldername.text.toString()) ;
Get.back();
//Navigator.of(context).pop();
// _animationController.reverse();
},
),
],
);
},
);
}
void _openFileType(BuildContext context) {
SKAlertDialog.show(
context: context,
type: SKAlertType.radiobutton,
radioButtonAry: {'Certificate': 1, 'Signature': 2, 'NID': 3, 'Passport': 4, 'Driving licence': 5},
title: 'Choose File category',
onCancelBtnTap: (value) {
print('Cancel Button Tapped');
Navigator.of(context).pop(false);
},
onRadioButtonSelection: (value) {
print('onRadioButtonSelection $value');
},
);
}
/* Future<void> initDiskSpace() async {
double diskSpace = 0;
diskSpace = await DiskSpace.getFreeDiskSpace;
if (!mounted) return;
setState(() {
_diskSpace = diskSpace;
});
}
*/
Future<void> _getStorgeInfo() async{
_freespace = await StorageCapacity.getFreeSpace;
//_freespacemb = await StorageCapacity.toMegaBytes(double.parse(_freespace.toString()));
_occupiedSpace = await StorageCapacity.getOccupiedSpace;
_totalSpace = await StorageCapacity.getTotalSpace;
}
}
NOTE: if I Hot Reload this page, it's working okay again
Please help.

the problem is, you are getting your data in the initState method, but your widget's build is being completed before initializing the data to _freespace, and that's why the error is appearing.
as a solution, I suggest removing _getStorgeInfo() call from initState, and implementing the following structure:
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
endDrawerEnableOpenDragGesture: false, // This way it will not open
// endDrawer: Drawer(),
drawer: new Drawer(
...
),
appBar: AppBar(
...
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(15.0),
child: FutureBuilder(
future: _getStorgeInfo(),
builder: (context, snapshot) {
if(snapshot.connectionState!=ConnectionState.Done) return CircularProgressIndicator();
return _widgetBody;
},
),
),
));
}

Related

How to keep drawer always open

I want to put a drawer like this in my Flutter app:
just like https://m3.material.io/develop/flutter
I'm using NavigationRail and it's said that a menu button can be added to open a navigation drawer. Does any knows how to add the menu button and the drawer?
menu button of NavigationRail
thanks.
It's a bit hard to use a regular Scaffold Drawer without the regular scaffold controls, as far as I can tell.
I came up with a solution for your problem, if I understood it correctly. Looks a lot like the spec site, needs a bit of styling.
Took the example from the NavigationRail documentation and added a Visibility widget. Now clicking on the destinations, you can show and hide their child widgets(drawer). No drawer animation though.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
home: const NavRailExample(),
);
}
}
class NavRailExample extends StatefulWidget {
const NavRailExample({super.key});
#override
State<NavRailExample> createState() => _NavRailExampleState();
}
class _NavRailExampleState extends State<NavRailExample> {
int _selectedIndex = 0;
NavigationRailLabelType labelType = NavigationRailLabelType.all;
bool showLeading = false;
bool showTrailing = false;
double groupAligment = -1.0;
bool _isClosed = false;
Widget _getWidget(int index) {
switch (index) {
case 1:
return GestureDetector(
child: const Text('Tap!'),
onTap: () => setState(() {
_isClosed = true;
}),
);
case 2:
return const Text('empty');
default:
return ListView(
children: const [
ExpansionTile(
title: Text('whatev'),
children: [Text('1'), Text('2')],
),
ListTile(
title: Text('adfafdafaf'),
)
],
);
}
}
Widget _getPage(int index) {
switch (index) {
case 1:
return const Center(child: Text('sheeesh'));
case 2:
return const Center(child: Text('empty'));
default:
return const Center(child: Text('yolo'),);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: <Widget>[
NavigationRail(
selectedIndex: _selectedIndex,
groupAlignment: groupAligment,
onDestinationSelected: (int index) {
setState(() {
_isClosed = (_selectedIndex == index || _isClosed)
? !_isClosed
: _isClosed;
_selectedIndex = index;
});
},
labelType: labelType,
leading: showLeading
? FloatingActionButton(
elevation: 0,
onPressed: () {
// Add your onPressed code here!
},
child: const Icon(Icons.add),
)
: const SizedBox(),
trailing: showTrailing
? IconButton(
onPressed: () {
// Add your onPressed code here!
},
icon: const Icon(Icons.more_horiz_rounded),
)
: const SizedBox(),
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.favorite_border),
selectedIcon: Icon(Icons.favorite),
label: Text('First'),
),
NavigationRailDestination(
icon: Icon(Icons.bookmark_border),
selectedIcon: Icon(Icons.book),
label: Text('Second'),
),
NavigationRailDestination(
icon: Icon(Icons.star_border),
selectedIcon: Icon(Icons.star),
label: Text('Third'),
),
],
),
Visibility(
maintainState: false,
visible: !_isClosed,
child: Row(
children: [
const VerticalDivider(thickness: 1, width: 1),
SizedBox(
height: double.infinity,
width: 200,
child: _getWidget(_selectedIndex),
)
],
),
),
const VerticalDivider(thickness: 1, width: 1),
// This is the main content.
Expanded(
child: _getPage(_selectedIndex),
),
],
),
),
);
}
}

AnimatedList call - RangeError (index): Invalid value: Valid value range is empty: 0

I'm currently trying to familiarize myself with flutter. Therefore I am trying to create a todo list.
Everything is working fine except that I get an range Error when I mark the last todo as done:
The following RangeError was thrown building:
RangeError (index): Invalid value: Valid value range is empty: 0
When the exception was thrown, this was the stack:
#0 List.[] (dart:core-patch/growable_array.dart:264:36)
#1 _TodoListState.slideIt (package:todo_list/main.dart:38:25)
#2 _TodoListState.slideIt.<anonymous closure>.<anonymous closure> (package:todo_list/main.dart:59:59)
It seems like flutter is trying to build the slide It widget, even though the list is empty.
Sourcecode:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo List',
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: Colors.blue,
),
),
home: const TodoList());
}
}
class TodoList extends StatefulWidget {
const TodoList({Key? key}) : super(key: key);
#override
State<TodoList> createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
final _todos = <String>[];
final _doneTodos = <String>[];
final _biggerFont = const TextStyle(fontSize: 18);
Widget slideIt(BuildContext context, int index, animation) {
String item = _todos[index];
final alreadyDone = _doneTodos.contains(item);
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0),
).animate(animation),
child: SizedBox(
child: ListTile(
title: Text(
item,
style: _biggerFont,
),
trailing: Icon(
alreadyDone ? Icons.check_box : Icons.check_box_outline_blank,
color: alreadyDone ? Colors.blue : null,
semanticLabel: alreadyDone ? "Remove from done" : "Finish",
),
onTap: () {
_doneTodos.add(item);
listKey.currentState?.removeItem(
_todos.indexOf(item), (_, animation) => slideIt(context, index, animation),
duration: const Duration(milliseconds: 500));
_todos.remove(item);
},
)),
);
}
void _addTodo() {
Navigator.of(context).push(MaterialPageRoute<void>(builder: (context) {
final myController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: const Text('Add Todo'),
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onSubmitted: (textValue) {
listKey.currentState!.insertItem(_todos.length,
duration: const Duration(milliseconds: 500));
_todos.add(textValue);
Navigator.of(context).pop();
},
autofocus: true,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter a new Thing to do',
),
controller: myController,
),
const SizedBox(height: 16),
IconButton(
color: Colors.blue,
onPressed: () {
listKey.currentState!.insertItem(0,
duration: const Duration(milliseconds: 500));
_todos.insert(0, myController.text);
Navigator.of(context).pop();
},
icon: const Icon(Icons.add))
],
),
));
}));
}
void _showDoneTodo() {
Navigator.of(context).push(MaterialPageRoute<void>(builder: (context) {
final tiles = _doneTodos.map((string) {
return ListTile(
trailing: const Icon(
Icons.check_box,
semanticLabel: "Remove from done",
),
onTap: () {
listKey.currentState!.insertItem(_todos.length,
duration: const Duration(milliseconds: 500));
_todos.add(string);
_doneTodos.remove(string);
Navigator.of(context).pop();
},
title: Text(
string,
style: _biggerFont,
),
);
});
final divided = tiles.isNotEmpty
? ListTile.divideTiles(context: context, tiles: tiles).toList()
: <Widget>[];
return Scaffold(
appBar: AppBar(
title: const Text('Done Todos'),
),
body: ListView(children: divided),
);
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
actions: [
IconButton(
onPressed: _addTodo,
icon: const Icon(Icons.add),
tooltip: 'Add Todo',
),
IconButton(
onPressed: _showDoneTodo,
icon: const Icon(Icons.check_box),
tooltip: 'Show Done Todos',
)
],
),
body: AnimatedList(
key: listKey,
initialItemCount: _todos.length,
itemBuilder: (context, index, animation) {
return slideIt(context, index, animation);
},
),
);
}
}
The project ist based on the default flutter project, as well as the AnimatedList descriped by Pinkesh Darji here:
https://medium.com/flutter-community/how-to-animate-items-in-list-using-animatedlist-in-flutter-9b1a64e9aa16
How to get the error message: \
Start the app
Hit the "+" icon
Type something in and click enter or click the plus icon
click on the todo in the todo-list (to mark it as done)
-> error is thrown
The answer is a combination of the answer provided by Yeasin Sheikh and some of my experimenting and searching (especially this post was quite helpfull: Flutter: animate item removal in ListView)
The onTap function has to be changed like so:
onTap: () {
_doneTodos.add(item);
int removeIndex = _todos.indexOf(item);
String removedItem = _todos.removeAt(removeIndex);
listKey.currentState?.removeItem(
removeIndex, (_, animation) => slideIt(context,removedItem, index, animation),
duration: const Duration(milliseconds: 500));
},
Furthermore the rest of the code has to be updated accordingly.
Complete code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo List',
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: Colors.blue,
),
),
home: const TodoList());
}
}
class TodoList extends StatefulWidget {
const TodoList({Key? key}) : super(key: key);
#override
State<TodoList> createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
final GlobalKey<AnimatedListState> listKey = GlobalKey<AnimatedListState>();
final _todos = <String>[];
final _doneTodos = <String>[];
final _biggerFont = const TextStyle(fontSize: 18);
Widget slideIt(BuildContext context, String? removedItem, int index, animation) {
String item = removedItem ?? _todos[index];
final alreadyDone = _doneTodos.contains(item);
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0),
).animate(animation),
child: SizedBox(
child: ListTile(
title: Text(
item + _todos.length.toString(),
style: _biggerFont,
),
trailing: Icon(
alreadyDone ? Icons.check_box : Icons.check_box_outline_blank,
color: alreadyDone ? Colors.blue : null,
semanticLabel: alreadyDone ? "Remove from done" : "Finish",
),
onTap: () {
_doneTodos.add(item);
int removeIndex = _todos.indexOf(item);
String removedItem = _todos.removeAt(removeIndex);
listKey.currentState?.removeItem(
removeIndex, (_, animation) => slideIt(context,removedItem, index, animation),
duration: const Duration(milliseconds: 500));
},
)),
);
}
void _addTodo() {
Navigator.of(context).push(MaterialPageRoute<void>(builder: (context) {
final myController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: const Text('Add Todo'),
),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
onSubmitted: (textValue) {
listKey.currentState!.insertItem(_todos.length,
duration: const Duration(milliseconds: 500));
_todos.add(textValue);
Navigator.of(context).pop();
},
autofocus: true,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter a new Thing to do',
),
controller: myController,
),
const SizedBox(height: 16),
IconButton(
color: Colors.blue,
onPressed: () {
listKey.currentState!.insertItem(0,
duration: const Duration(milliseconds: 500));
_todos.insert(0, myController.text);
Navigator.of(context).pop();
},
icon: const Icon(Icons.add))
],
),
));
}));
}
void _showDoneTodo() {
Navigator.of(context).push(MaterialPageRoute<void>(builder: (context) {
final tiles = _doneTodos.map((string) {
return ListTile(
trailing: const Icon(
Icons.check_box,
semanticLabel: "Remove from done",
),
onTap: () {
listKey.currentState!.insertItem(_todos.length,
duration: const Duration(milliseconds: 500));
_todos.add(string);
_doneTodos.remove(string);
Navigator.of(context).pop();
},
title: Text(
string,
style: _biggerFont,
),
);
});
final divided = tiles.isNotEmpty
? ListTile.divideTiles(context: context, tiles: tiles).toList()
: <Widget>[];
return Scaffold(
appBar: AppBar(
title: const Text('Done Todos'),
),
body: ListView(children: divided),
);
}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
actions: [
IconButton(
onPressed: _addTodo,
icon: const Icon(Icons.add),
tooltip: 'Add Todo',
),
IconButton(
onPressed: _showDoneTodo,
icon: const Icon(Icons.check_box),
tooltip: 'Show Done Todos',
)
],
),
body: AnimatedList(
key: listKey,
initialItemCount: _todos.length,
itemBuilder: (context, index, animation) {
return slideIt(context, null, index, animation);
},
),
);
}
}
Remove _todos.remove(item); on onTap and it will work.
The listKey.currentState?.removeItem( also removing item with animation, and it misses the item while animating. So we can use animationState to remove item(data)from it.
On your slideIt
Widget slideIt(BuildContext context, int index, Animation<double> animation) {
// ....
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: const Offset(0, 0),
).animate(animation)
..addStatusListener((status) {
if (status == AnimationStatus.dismissed) {
_todos.remove(item);
}
}),

Why isn't Navigator.pop() refreshing data?

Hi guys I'm trying to build an app with flutter, so I have two screens HomeScreen() and RoutineScreen(). The first one is a Scaffold and in the body has a child Widget (a ListView called RoutinesWidget()) with all the routines. And the second one is to create a routine. The thing is, that when I create the routine, I use a button to pop to the HomeScreen() but it doesn't refresh the ListView (I'm guessing that it's because when I use Navigator.pop() it refreshes the Scaffold but not the child Widget maybe?)
HomeScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Widgets/routines_widget.dart';
import 'package:workout_time/Widgets/statistics_widget.dart';
import 'package:workout_time/Screens/settings_screen.dart';
import 'package:workout_time/Screens/routine_screen.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
List<Widget> _views = [
RoutinesWidget(),
StatisticsWidget(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kThirdColor,
appBar: AppBar(
leading: Icon(Icons.adb),
title: Text("Workout Time"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen()))),
],
),
body: _views[_selectedIndex],
floatingActionButton: (_selectedIndex == 1)
? null
: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(null)));
setState(() {});
},
child: Icon(
Icons.add,
color: kSecondColor,
size: 30.0,
),
elevation: 15.0,
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
bottomItems(Icon(Icons.fitness_center_rounded), "Routines"),
bottomItems(Icon(Icons.leaderboard_rounded), "Statistics"),
],
currentIndex: _selectedIndex,
onTap: (int index) => setState(() => _selectedIndex = index),
),
);
}
}
BottomNavigationBarItem bottomItems(Icon icon, String label) {
return BottomNavigationBarItem(
icon: icon,
label: label,
);
}
RoutinesWidget() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Services/db_crud_service.dart';
import 'package:workout_time/Screens/routine_screen.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Models/routine_model.dart';
class RoutinesWidget extends StatefulWidget {
#override
_RoutinesWidgetState createState() => _RoutinesWidgetState();
}
class _RoutinesWidgetState extends State<RoutinesWidget> {
DBCRUDService helper;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: helper.getRoutines(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Routine routine = Routine.fromMap(snapshot.data[index]);
return Card(
margin: EdgeInsets.all(1.0),
child: ListTile(
leading: CircleAvatar(
child: Text(
routine.name[0],
style: TextStyle(
color: kThirdOppositeColor,
fontWeight: FontWeight.bold),
),
backgroundColor: kAccentColor,
),
title: Text(routine.name),
subtitle: Text(routine.exercises.join(",")),
trailing: IconButton(
icon: Icon(Icons.delete_rounded),
color: Colors.redAccent,
onPressed: () {
setState(() {
helper.deleteRoutine(routine.id);
});
},
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(routine))),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
color: kSecondColor,
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
RoutineScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Models/routine_model.dart';
import 'package:workout_time/Widgets/type_card_widget.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Services/db_crud_service.dart';
class RoutineScreen extends StatefulWidget {
final Routine _routine;
RoutineScreen(this._routine);
#override
_RoutineScreenState createState() => _RoutineScreenState();
}
class _RoutineScreenState extends State<RoutineScreen> {
DBCRUDService helper;
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
bool _type = true;
int _cycles = 1;
int _restBetweenExercises = 15;
int _restBetweenCycles = 60;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
title: widget._routine != null
? Text(widget._routine.name)
: Text("Create your routine"),
actions: [
IconButton(
icon: Icon(Icons.done_rounded),
onPressed: createRoutine,
)
],
bottom: TabBar(
tabs: [
Tab(
text: "Configuration",
),
Tab(
text: "Exercises",
),
],
),
),
body: TabBarView(children: [
//_routine == null ? ConfigurationNewRoutine() : Text("WIDGET N° 1"),
ListView(
children: [
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"Name:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
controller: _nameController,
),
),
],
),
),
SizedBox(
height: 20.0,
),
Card(
margin: EdgeInsets.all(15.0),
color: kSecondColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
"Type",
style: TextStyle(fontSize: 25.0),
),
Row(
children: [
Expanded(
child: TypeCard(
Icons.double_arrow_rounded,
_type == true ? kFirstColor : kThirdColor,
() => setState(() => _type = true),
"Straight set",
),
),
Expanded(
child: TypeCard(
Icons.replay_rounded,
_type == false ? kFirstColor : kThirdColor,
() => setState(() => _type = false),
"Cycle",
),
),
],
),
],
),
),
),
SizedBox(
height: 20.0,
),
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"N° cycles:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: Text("Hello"),
),
],
),
),
SizedBox(
height: 20.0,
),
],
),
Text("WIDGET N° 2"),
]),
),
);
}
void createRoutine() {
List<String> _exercises = ["1", "2"];
List<String> _types = ["t", "r"];
List<String> _quantities = ["30", "20"];
Routine routine = Routine({
'name': _nameController.text,
'description': "_description",
'type': _type.toString(),
'cycles': 1,
'numberExercises': 2,
'restBetweenExercises': 15,
'restBetweenCycles': 60,
'exercises': _exercises,
'types': _types,
'quantities': _quantities,
});
setState(() {
helper.createRoutine(routine);
Navigator.pop(context);
});
}
}
Any idea what can I do to make it work? Thank you
Make it simple
use Navigator.pop() twice
so that the current class and old class in also removed
from the stack
and then use Navigator.push()
When you push a new Route, the old one still stays in the stack. The new route just overlaps the old one and forms like a layer above the old one. Then when you pop the new route, it will just remove the layer(new route) and the old route will be displayed as it was before.
Now you must be aware the Navigator.push() is an asynchronous method and returns a Future. How it works is basically when you perform a Navigator.push(), it will push the new route and will wait for it to be popped out. Then when the new route is popped, it returns a value to the old one and that when the future callback will be executed.
Hence the solution you are looking for is add a future callback like this after your Navigator.push() :
Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen())
).then((value){setState(() {});}); /// A callback which is executed after the new route will be popped. In that callback, you simply call a setState and refresh the page.

Display different fab depending on active View of TabBarView

If PageA is selected, fabA should be displayed. If PageB is selected, fabB respectively.
Whats the best way to achieve this?
var fabA = FloatingActionButton();
var fabB = FloatingActionButton();
...
Scaffold(
body: TabBarView(children: [
PageA(),
PageB(),
]),
floatingActionButton: fabA,
)
You can copy paste run full code below
You can declare a List<FloatingActionButton> and return based on tabController.index
code snippet
_tabController = TabController(vsync: this, length: choices.length)
..addListener(() {
if (_tabController.indexIsChanging) {
print(
"tab is animating. from active (getting the index) to inactive(getting the index) ");
} else {
setState(() {});
...
floatingActionButton: floatButton[_tabController.index],
...
List<FloatingActionButton> floatButton = [
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.navigation),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.share),
backgroundColor: Colors.green,
),
working demo
full code
import 'package:flutter/material.dart';
class AppBarBottomSample extends StatefulWidget {
#override
_AppBarBottomSampleState createState() => _AppBarBottomSampleState();
}
class _AppBarBottomSampleState extends State<AppBarBottomSample>
with SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: choices.length)
..addListener(() {
if (_tabController.indexIsChanging) {
print(
"tab is animating. from active (getting the index) to inactive(getting the index) ");
} else {
//tab is finished animating you get the current index
//here you can get your index or run some method once.
print(_tabController.index);
setState(() {});
}
});
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
void _nextPage(int delta) {
final int newIndex = _tabController.index + delta;
if (newIndex < 0 || newIndex >= _tabController.length) return;
_tabController.animateTo(newIndex);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('AppBar Bottom Widget'),
leading: IconButton(
tooltip: 'Previous choice',
icon: const Icon(Icons.arrow_back),
onPressed: () {
_nextPage(-1);
},
),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_forward),
tooltip: 'Next choice',
onPressed: () {
_nextPage(1);
},
),
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48.0),
child: Theme(
data: Theme.of(context).copyWith(accentColor: Colors.white),
child: Container(
height: 48.0,
alignment: Alignment.center,
child: TabPageSelector(controller: _tabController),
),
),
),
),
body: TabBarView(
controller: _tabController,
children: choices.map((Choice choice) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: ChoiceCard(choice: choice),
);
}).toList(),
),
floatingActionButton: floatButton[_tabController.index],
),
);
}
}
List<FloatingActionButton> floatButton = [
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.navigation),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.share),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.create),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.repeat),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.forward),
backgroundColor: Colors.green,
),
FloatingActionButton(
onPressed: () {
// Add your onPressed code here!
},
child: Icon(Icons.link),
backgroundColor: Colors.green,
)
];
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const <Choice>[
const Choice(title: 'CAR', icon: Icons.directions_car),
const Choice(title: 'BICYCLE', icon: Icons.directions_bike),
const Choice(title: 'BOAT', icon: Icons.directions_boat),
const Choice(title: 'BUS', icon: Icons.directions_bus),
const Choice(title: 'TRAIN', icon: Icons.directions_railway),
const Choice(title: 'WALK', icon: Icons.directions_walk),
];
class ChoiceCard extends StatelessWidget {
const ChoiceCard({Key key, this.choice}) : super(key: key);
final Choice choice;
#override
Widget build(BuildContext context) {
final TextStyle textStyle = Theme.of(context).textTheme.display1;
return Card(
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(choice.icon, size: 128.0, color: textStyle.color),
Text(choice.title, style: textStyle),
],
),
),
);
}
}
void main() {
runApp(AppBarBottomSample());
}

Snackbar in SimpleDialog Flutter

I faced an error code below when adding a snackbar to an on-pressed method in my Simpledialog.
[Scaffold.of() called with a context that does not contain a Scaffold.]
I would like to seek your advice on how to provide the correct context to resolve it.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new AlertApp()));
}
class AlertApp extends StatefulWidget {
#override
_AlertAppState createState() => _AlertAppState();
}
class _AlertAppState extends State<AlertApp> {
SimpleDialog _simdalog;
void sDialog(){
_simdalog = new SimpleDialog(
title: new Text("Add To Shopping Cart"),
children: <Widget>[
new SimpleDialogOption(
child: new Text("Yes"),
onPressed: (){
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
},
),
new SimpleDialogOption(
child: new Text("Close"),
onPressed:() {Navigator.pop(context);},
),
],
);
showDialog(context: context, builder: (BuildContext context){
return _simdalog;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: new Text("Add to Shopping Cart [Simple]"),
onPressed:(){
sDialog();
}),
],
),
),
);
}
}
Solution 1: as Mazin Ibrahim mentioned in comments Scaffold.of() called with a context that does not contain a Scaffold
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
...
Scaffold(
key: _scaffoldKey,
...
onPressed: () {
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text('Purchase Successful'),
duration: Duration(seconds: 3),
));
}
Solution 2:
With package flushbar, you can also display notification on top
Flushbar link : https://github.com/AndreHaueisen/flushbar
Another suggestion to use flushbar How to show snackbar after navigator.pop(context) in Flutter?
Flushbar(
title: "Hey Ninja",
message: "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
flushbarPosition: FlushbarPosition.TOP,
flushbarStyle: FlushbarStyle.FLOATING,
reverseAnimationCurve: Curves.decelerate,
forwardAnimationCurve: Curves.elasticOut,
backgroundColor: Colors.red,
boxShadows: [BoxShadow(color: Colors.blue[800], offset: Offset(0.0, 2.0), blurRadius: 3.0)],
backgroundGradient: LinearGradient(colors: [Colors.blueGrey, Colors.black]),
isDismissible: false,
duration: Duration(seconds: 4),
icon: Icon(
Icons.check,
color: Colors.greenAccent,
),
mainButton: FlatButton(
onPressed: () {},
child: Text(
"CLAP",
style: TextStyle(color: Colors.amber),
),
),
showProgressIndicator: true,
progressIndicatorBackgroundColor: Colors.blueGrey,
titleText: Text(
"Hello Hero",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0, color: Colors.yellow[600], fontFamily: "ShadowsIntoLightTwo"),
),
messageText: Text(
"You killed that giant monster in the city. Congratulations!",
style: TextStyle(fontSize: 18.0, color: Colors.green, fontFamily: "ShadowsIntoLightTwo"),
),
)..show(context);
You can return a bool from the showDialog method and use that to determine whether to show the snackbar:
void main() {
runApp(MaterialApp(
home: AlertApp(),
));
}
class AlertApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MyShoppingButton(),
],
),
),
);
}
}
// Separate out the button from _AlertAppState so that the call to
// showSnackBar comes from a different BuildContext
class MyShoppingButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Add to Shopping Cart [Simple]"),
// Use an async onPressed method so that we can wait for the
// result from the dialog before deciding whether to show the snackbar
onPressed: () async {
bool result = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return MyShoppingDialog();
},
);
// Check if result is null below as Flutter will throw Exception if
// tries determining whether to enter an if branch will a null boolean
if (result != null && result) {
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
}
},
);
}
}
class MyShoppingDialog extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("Add To Shopping Cart"),
children: <Widget>[
SimpleDialogOption(
child: Text("Yes"),
onPressed: () {
// Pop with a result of true so that MyShoppingButton
// knows to show snackbar. In any other case
// (including the user dismissing the dialog), MyShoppingButton
// null receive null, and so will not show the snackbar
Navigator.of(context).pop(true);
},
),
SimpleDialogOption(
child: Text("Close"),
onPressed: () {
Navigator.pop(context);
},
),
],
);
}
}
You should create a Scaffold widget inside of showDialog and a Builder widget as child of the Scaffold and pass context as parameter.
void sDialog({BuildContext context}){
_simdalog = new SimpleDialog(
title: new Text("Add To Shopping Cart"),
children: <Widget>[
new SimpleDialogOption(
child: new Text("Yes"),
onPressed: (){
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
},
),
new SimpleDialogOption(
child: new Text("Close"),
onPressed:() {Navigator.pop(context);},
),
],
);
showDialog(context: context, builder: (BuildContext context){
return GestureDetector(
onTap: (){Navigator.of(context).pop();},
child: Scaffold(
body: Builder(
builder: (context){
return _simdalog(context: context);
}
),
),);
});
}