Flutter ExpansionTile from json - flutter

I have a flutter app that displays a list of users from a RESTapi in a list view. However I would like to show the list of users in an expansion tile so as to show more details about them. Here is the method for fetching the data:
final String apiURL = 'https://jsonplaceholder.typicode.com/users';
Future<List<Users>> fetchJSONData() async {
var jsonResponse = await http.get(Uri.parse(apiURL));
if (jsonResponse.statusCode == 200) {
final jsonItems =
json.decode(jsonResponse.body).cast<Map<String, dynamic>>();
List<Users> usersList = jsonItems.map<Users>((json) {
return Users.fromJson(json);
}).toList();
return usersList;
} else {
throw Exception('Failed to load data from internet');
}
}
This is the code used to display the list of users:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample Customer List'),
backgroundColor: Theme.of(context).accentColor,
),
body: _buildExpanded());
}
Widget _showListTile() {
return FutureBuilder<List<Users>>(
future: fetchJSONData(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
return ListView(
children: snapshot.data
.map(
(user) => Slidable(
actionPane: SlidableScrollActionPane(),
actions: <Widget>[
IconSlideAction(
caption: 'Archive',
color: Colors.blue,
icon: Icons.archive,
onTap: () => print('Archiving'),
),
IconSlideAction(
caption: 'Share',
color: Colors.green,
icon: Icons.share,
onTap: () => print('Share'),
)
],
actionExtentRatio: 1 / 5,
child: ListTile(
title: Text(user.name),
onTap: () {
print(user.name);
},
subtitle: Text(user.phoneNumber),
leading: CircleAvatar(
backgroundColor: Colors.green,
child: Text(user.name[0],
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
)),
),
),
secondaryActions: <Widget>[
IconSlideAction(
caption: 'More',
color: Colors.black45,
icon: Icons.more_horiz,
onTap: () => print('More'),
),
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => print('Delete'),
),
],
),
)
.toList(),
);
},
);
}
What do I need to do to implement an expansion tile is this app?

For Example
ExpansionTile(
title: Text(user.name),
subtitle: Text(user.phoneNumber),
leading: CircleAvatar(
backgroundColor: Colors.green,
child: Text(user.name[0],
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
)),
),
children: [
Text(
"${user.email}",
style: TextStyle(fontSize: 18),
),
Text(
"${user.address}",
style: TextStyle(fontSize: 18),
),
// ...
// other information you want to show
])
OR you can push other page to show detail.

Related

Navigator.pop shows me a black screen

It's been a while since I've been blocking backtracking on my flutter application, I tried the Navigator.pop (context) but I still ran into a black screen, I searched the forums for success but I'm still stuck. I want that when I click on return that it brings me back to my previous page without initializing the page
import 'package:MerchantIsland/log/database.dart';
import 'package:MerchantIsland/pages/home.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class productPage extends StatefulWidget {
final String productId;
const productPage({Key key, this.productId}) : super(key: key);
#override
_productPageState createState() => _productPageState();}
class _productPageState extends State<productPage> {
#override
Widget build(BuildContext context) {
ProductService productService = ProductService();
return WillPopScope(
onWillPop: (){
MovetoPreviousScreen();},
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: (){
MovetoPreviousScreen();
},
),
centerTitle: true,
title: Text(
'Merchant island',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
body: Stack(
children: [
FutureBuilder(
future: productService.ProductData.doc(widget.productId).get(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
if(snapshot.connectionState==ConnectionState.done){
Map<String, dynamic> documentData=snapshot.data.data();
return ListView(
children: [
Container(
height: 400.0,
child: Image.network(
"${documentData['pictures'][0]}",
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0,horizontal: 24.0),
child: Text('${documentData['productName']}'??"Nom du produit",
style:TextStyle(
fontSize: 28.0,
fontWeight: FontWeight.bold,
) ,),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0,horizontal: 24.0),
child: Text('${documentData['price']}',
style:TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.bold,
color:Colors.red
) ),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0,horizontal: 24.0),
child: Text('${documentData['description']}',
style:TextStyle(
fontSize: 18
) ),
)
],
);
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}),
],
),
),
); }
// ignore: non_constant_identifier_names
void MovetoPreviousScreen() {
Navigator.of(context).pop(); }}
return page
import 'package:MerchantIsland/log/database.dart';
import 'package:MerchantIsland/log/loginUI.dart';
import 'package:MerchantIsland/pages/Sellproduct.dart';
import 'package:MerchantIsland/pages/bidPage.dart';
import 'package:MerchantIsland/products/productPage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:MerchantIsland/pages/profil.dart';
import 'package:MerchantIsland/pages/balance.dart';
import 'package:MerchantIsland/pages/Settings.dart';
import 'package:google_sign_in/google_sign_in.dart';
// ignore: camel_case_types
class home extends StatefulWidget {
#override
_homeState createState() => _homeState();}
// ignore: camel_case_types
class _homeState extends State<home> {
ProductService productService = ProductService();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
title: Text("Merchant Island"),
actions: [
IconButton(
icon: Icon(Icons.search, color: Colors.white,), onPressed: null,),
],
),
body: Stack(
children: [
FutureBuilder<QuerySnapshot>(
future: productService.ProductData.get(),
builder:(context,snapshot){
if(snapshot.hasError){
return Scaffold(
body: Center(
child: Text("Error: ${snapshot.error}"),
),
);
}
if(snapshot.connectionState==ConnectionState.done){
return Container(
child: ListView(
children: snapshot.data.docs.map((documents){
return Container(
child: GestureDetector(
onTap: (){
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context)=>productPage(productId: documents.id,)));
},
child: Container(
child: productCard(documents.data()["productName"],documents.data()["category"], documents.data()["price"],documents.data()["pictures"]) ,
),
),
);
}).toList(),
),
);
}
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}),
],
),
drawer: BDrawer(context),
); }}
// ignore: non_constant_identifier_names
Drawer BDrawer(BuildContext context) {
FirebaseAuth _auth = FirebaseAuth.instance;
GoogleSignIn _googleSignIn = GoogleSignIn();
Future <void> signOut() async {
await _auth.signOut();
await _googleSignIn.disconnect();
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => LoginUI()));}
return Drawer(
child: ListView(
children: [
UserAccountsDrawerHeader(
accountName: Text('TITAN', style: TextStyle(
fontSize: 18.0,
),),
accountEmail: Text('philippetankoano#gmail.com'),
currentAccountPicture: GestureDetector(
child: CircleAvatar(
backgroundColor: Colors.grey,
child: Icon(
Icons.person, color: Colors.white
),
),
),
otherAccountsPictures: [
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => profil(),)),
child: (
Icon(
Icons.mode_edit, size: 30.0,
)
),
)
],
),
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => balancepage())),
child: ListTile(
leading: Icon(
Icons.account_balance, size: 30.0, color: Colors.blue,),
title: Text(' Balance', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => home())),
child: ListTile(
leading: Icon(Icons.home, size: 30.0, color: Colors.blue,),
title: Text(' Home', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SellProduct())),
child: ListTile(
leading: Icon(
Icons.account_balance, size: 30.0, color: Colors.blue,),
title: Text(' Sell product', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () {},
child: ListTile(
leading: Icon(
Icons.shopping_basket_outlined, size: 40.0,
color: Colors.blue,),
title: Text('Sale', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.category, size: 30.0, color: Colors.blue,),
title: Text(' Categories', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => bidPage())),
child: ListTile(
leading: Icon(
Icons.event_available, size: 40.0, color: Colors.blue,),
title: Text(' Bid', style: TextStyle(
fontSize: 18.0,
),),
),
),
Divider(),
InkWell(
onTap: () =>
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => Settingpage(),)),
child: ListTile(
leading: Icon(Icons.settings, size: 30.0, color: Colors.blue,),
title: Text(' Setting', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.help, size: 30.0, color: Colors.blue,),
title: Text(' Help', style: TextStyle(
fontSize: 18.0,
),),
),
),
InkWell(
onTap: () async {
signOut();
},
child: ListTile(
leading: Icon(Icons.exit_to_app, size: 30.0, color: Colors.blue,),
title: Text(' Deconnexion', style: TextStyle(
fontSize: 18.0,
),),
),
),
]
), );}
Padding productCard(String name,String category,String price,List imageUrl){
return Padding(
padding: const EdgeInsets.all(6.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10.0),
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(-2,-1),
blurRadius: 5
)
] ),
child: GestureDetector(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(7.0),
child: Image.network(
"${imageUrl[0]}",
height: 500,
width: 450,
),
),
],
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(' $name ',style: TextStyle(fontSize: 24.0,fontWeight: FontWeight.bold)),
Text('$price ',style: TextStyle(fontSize: 20.0,fontWeight: FontWeight.bold,color: Colors.red)),
],
),
),
Text('Categorie: $category \n',style: TextStyle(fontSize: 24.0,)),
],
),
), ),);}
Method One(Recommended)
First check if there is more than one Material App in your project if found then remove all except the root one (Child of MyApp) if that does not work or you have only one MaterialApp in your project then only try second method
Method Two(Try only if method one doesn't solved your issue or you don't have more than one MaterialApp in your project)
Replace Navigator.of(context).pop(); with Navigator.of(context,rootNavigator:true).pop(context)
If you got a black screen, it's probably because you are popping the only screen in your stack.
If you want that Navigator.of(context).pop() remove only the last screen, you have to display the current screen with a Navigator.of(context).push()
When clicking on the custom back icon Leading with in flutter
Replace Navigator.of(context).pop(); with Navigator.of(context,rootNavigator:true).pop(context)
appBar: AppBar(
automaticallyImplyLeading: true,
backgroundColor: ColorConstants.kBlackColor,
leading: BackButton(
color: ColorConstants.kWhiteColor,
onPressed: (){
Navigator.of(context,rootNavigator:true).pop(context);
},
),
),
In my case the problem was having some Navigator.pop(context) inside other callback functions.
When I clicked the back button, a socket would close, and consequently call the callback function (that also called pop()), so I would have Navigator.pop(context) called more than one time. And since my Page widget was the second element of the stack, calling it 2 times would pop also the root, therefore generating the black screen.
What I did to fix it, even though I think there might be better solutions, was substituting all the Navigator.pop(context) calls with the following:
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
Another possible solution would be to initialize a bool variable (or a some kind of counter) and then call pop() only if that variable is true, then set it to false.:
// init
bool variable = true;
if (variable) {
Navigator.pop(context);
variable = false;
}

Show drawer over bottom navigation bar in Flutter

I have a drawer in a appbar and need to show it over the bottom navigation bar but can't put both in the same view, I don't know exactly how to do this.
This is what it looks like now and this is what it needs to look like.
This is part of the code of the view where the appbar is
class ContactsPage extends StatefulWidget {
final String title;
final String token;
final String qr;
String code;
final String name;
ContactsPage({this.name, this.token, this.qr, this.code, Key key, this.title})
: super(key: key);
#override
_ContactsPageState createState() => _ContactsPageState();
}
class _ContactsPageState extends State<ContactsPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
List<Contact> contactList;
bool showHorizontalBar = false;
bool ready = false;
#override
void initState() {
super.initState();
var userService = new UserService();
userService.getContacts(widget.token).then((value) => {
print(value),
if (value == '0')
{
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => LoginPage()))
}
else if (value == '3')
{
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => LoginPage()))
}
else
{
setState(() {
contactList = value;
ready = true;
})
},
print(contactList),
});
}
void showMessage(String message, [MaterialColor color = Colors.red]) {
_scaffoldKey.currentState..removeCurrentSnackBar();
_scaffoldKey.currentState.showSnackBar(
new SnackBar(backgroundColor: color, content: new Text(message)));
}
_navigateAndDisplaySelection(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scanner(
qr: widget.qr,
token: widget.token,
)),
);
if (result != null) {
showMessage('$result', Colors.red);
}
}
Widget _addPerson() {
return FloatingActionButton(
onPressed: () {
_navigateAndDisplaySelection(context);
},
child: Icon(Icons.group_add),
backgroundColor: Color(0xff83bb37),
);
}
Widget buildMenuIcon() {
return IconButton(
icon: Icon(showHorizontalBar ? Icons.close : Icons.more_horiz),
onPressed: () {
setState(() {
showHorizontalBar = !showHorizontalBar;
});
},
);
}
Widget _simplePopup() => PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
child: Row(
children: <Widget>[
IconButton(
icon: Icon(
Icons.delete,
color: Color(0xff83bb37),
),
onPressed: () => {},
),
IconButton(
icon: Icon(
Icons.favorite,
color: Color(0xff83bb37),
),
onPressed: () => {},
),
IconButton(
icon: Icon(
Icons.mail,
color: Color(0xff83bb37),
),
onPressed: () => {},
),
IconButton(
icon: Icon(
Icons.calendar_today,
color: Color(0xff83bb37),
),
onPressed: () => {},
),
IconButton(
icon: Icon(
Icons.call,
color: Color(0xff83bb37),
),
onPressed: () => {},
),
],
),
)
],
icon: Icon(
Icons.more_horiz,
size: 20,
color: Color(0xff4d4c48),
),
);
Widget _card(String first_name, String last_name, String email) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: [
SizedBox(
height: 5.0,
),
ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(13.0),
child: Image.asset(
'assets/images/mujer.jpg',
width: 60.0,
height: 70.0,
fit: BoxFit.cover,
),
),
title: Row(
children: [
Text(
first_name,
style: TextStyle(
fontWeight: FontWeight.bold, color: Color(0xff4d4c48)),
),
SizedBox(width: 5.0),
Text(
last_name,
style: TextStyle(
fontWeight: FontWeight.bold, color: Color(0xff4d4c48)),
)
],
),
subtitle: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
email,
style: TextStyle(color: Color(0xff4d4c48)),
),
SizedBox(
height: 5.0,
),
Text(
'Prowebglobal',
style: TextStyle(
color: Color(0xff4d4c48), fontWeight: FontWeight.w600),
),
],
),
),
trailing: _simplePopup(),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ContactDetails(token: widget.token, email: email)));
},
),
SizedBox(
height: 20.0,
),
],
),
);
}
Widget textContainer(String string, Color color) {
return new Container(
child: new Text(
string,
style: TextStyle(
color: color, fontWeight: FontWeight.normal, fontSize: 16.0),
textAlign: TextAlign.start,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
margin: EdgeInsets.only(bottom: 10.0),
);
}
Widget _titulo() {
return new Container(
alignment: Alignment.topLeft,
padding: EdgeInsets.only(left: 20.0),
child: new Text(
'Contactos',
style: TextStyle(
color: Color(0xff83bb37),
fontWeight: FontWeight.bold,
fontSize: 25.0),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
drawer: NavDrawer(
token: widget.token,
),
appBar: AppBar(
centerTitle: true,
backgroundColor: Color(0xfff0f0f0),
title: Image.asset(
'assets/images/logo-iso.png',
height: 50.0,
fit: BoxFit.contain,
alignment: Alignment.center,
),
iconTheme: new IconThemeData(color: Color(0xff707070)),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
},
),
]),
body: Column(children: [
SizedBox(
height: 20.0,
),
Expanded(
flex: 2,
child: _titulo(),
),
Expanded(
flex: 20,
child: Container(
child: ready
? ListView(
children: contactList
.map(
(Contact contact) => _card("${contact.first_name}",
"${contact.last_name}", "${contact.email}"),
)
.toList())
: Center(
child: Image.asset(
"assets/images/logo-gif.gif",
height: 125.0,
width: 125.0,
),
),
),
),
]),
floatingActionButton: _addPerson(),
);
}
}
And this is where de bottom navigation menu is
class HomePage extends StatefulWidget {
HomePage({
this.token,
this.code,
Key key,
}) : super(key: key);
final String token;
final String code;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String currentPage = 'contacts';
changePage(String pageName) {
setState(() {
currentPage = pageName;
});
}
#override
Widget build(BuildContext context) {
final Map<String, Widget> pageView = <String, Widget>{
"contacts": ContactsPage(
code: widget.code,
token: widget.token,
),
"profile": ProfilePage(
token: widget.token,
),
};
return Scaffold(
body: pageView[currentPage],
bottomNavigationBar: new BottomNavigationDot(
paddingBottomCircle: 21,
color: Colors.black.withOpacity(0.5),
backgroundColor: Colors.white,
activeColor: Colors.black,
items: [
new BottomNavigationDotItem(
icon: Icons.home,
onTap: () {
changePage("contacts");
}),
new BottomNavigationDotItem(icon: Icons.brush, onTap: () {}),
new BottomNavigationDotItem(icon: Icons.notifications, onTap: () {}),
new BottomNavigationDotItem(icon: Icons.favorite, onTap: () {}),
new BottomNavigationDotItem(
icon: Icons.person,
onTap: () {
changePage("profile");
}),
],
milliseconds: 400,
),
);
}
}
Edit:
I have thought on putting de appbar in the same level as de bottom navigation bar but I need to put different options on the appbar depending on the view so I thought on using diferent appbars, that's why I wanted it on the same level as the view.

Flutter: Prevent Future Builder from firing

I am new to flutter and I'm trying to build a simple app. Whenever I update profile details from EditProfileScreen and try to return to ProfileScreen through LandingScreen, FutureBuilder keeps on firing and LogoScreen appears. How to avoid that?
I tried of using Navigator pop but my new data is not updated in that case. I can't Navigate to ProfileScreen directly as I don't want to loose my bottom navigation bar. Can anybody suggest me a right way to do this?
LandingScreen():
class LandingScreen extends StatefulWidget {
final int index;
LandingScreen({this.index});
#override
_LandingScreenState createState() => _LandingScreenState();
}
class _LandingScreenState extends State<LandingScreen> {
int _currentIndex = 0;
List<Futsal> list;
List<Search> listHistory;
List<Futsal> futsalList;
Future<dynamic> loadDataFuture;
final List<Widget> _children = [
HomePage(),
ExploreScreen(),
ProfileDetails(),
];
#override
void initState() {
onTappedBar(widget.index);
loadDataFuture = getFutureData();
super.initState();
}
void onTappedBar(int index) {
setState(() {
_currentIndex = index;
});
}
Future getFutureData() async {
listHistory = await fetchSearchs();
futsalList = await fetchFutsals();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: new FutureBuilder(
future: loadDataFuture,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return new Text('Please close the application and Try Again.');
case ConnectionState.waiting:
return LogoScreen();
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
automaticallyImplyLeading: false,
backgroundColor: kPrimaryLightColor,
title: Text(
'letsfutsal',
style: TextStyle(
fontWeight: FontWeight.bold,
color: kPrimaryColor,
),
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
color: kPrimaryColor,
onPressed: () {
showSearch(
context: context,
delegate: SearchScreen(
futsalList: futsalList,
listHistory: listHistory));
},
),
],
),
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTappedBar,
currentIndex: _currentIndex,
selectedItemColor: kPrimaryColor,
unselectedItemColor: Colors.black38,
showSelectedLabels: false,
showUnselectedLabels: false,
items: [
BottomNavigationBarItem(
icon: new FaIcon(FontAwesomeIcons.home),
title: new Text(''),
),
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.safari),
title: Text(''),
),
BottomNavigationBarItem(
icon: FaIcon(FontAwesomeIcons.solidUserCircle),
title: Text(''),
),
],
),
);
}
},
),
);
}
}
ProfileScreen():
class ProfileDetails extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CustomUserProvider()),
ChangeNotifierProvider(create: (context) => MyBookingsProvider()),
],
child: SafeArea(
child: Scaffold(
backgroundColor: kPrimaryLightColor,
appBar: new AppBar(
automaticallyImplyLeading: false,
elevation: 0,
title: Text(
'Profile',
style: TextStyle(
fontWeight: FontWeight.bold,
color: kPrimaryColor,
),
),
actions: [
FlatButton.icon(
onPressed: () {},
icon: Icon(
FontAwesomeIcons.signOutAlt,
color: kPrimaryColor,
),
label: Text(
'Log Out',
style: TextStyle(
color: kPrimaryColor,
),
),
),
],
),
body: new Container(
padding: const EdgeInsets.all(15.0),
child: new Center(
child: new Column(
children: <Widget>[
UserDetails(),
MyBookingsList(),
],
),
),
),
),
),
);
}
}
class UserDetails extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final userProvider = Provider.of<CustomUserProvider>(context);
if (userProvider.user.length == 0) {
return Container();
} else {
return Column(
children: <Widget>[
new CircularImageContainer(
radius: 50,
imageUrl: "assets/images/profile.png",
),
SizedBox(height: size.height * 0.03),
Text(
userProvider.user[0].name != null ? userProvider.user[0].name : "",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
),
),
SizedBox(height: size.height * 0.01),
Text(
userProvider.user[0].address != null
? userProvider.user[0].address
: "",
),
FlatButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return EditProfileScreen(
user: userProvider.user[0],
);
},
),
);
},
icon: Icon(
Icons.edit,
color: kPrimaryColor,
),
label: Text(
'Edit Profile',
style: TextStyle(
color: kPrimaryColor,
),
),
),
],
);
}
}
}
class MyBookingsList extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final bookingsProvider = Provider.of<MyBookingsProvider>(context);
if (bookingsProvider.bookings.length == 0) {
return Container();
} else {
return Column(
children: <Widget>[
ScrollListContainer(
text: "My Bookings",
size: size,
),
ListView.builder(
shrinkWrap: true,
itemCount: bookingsProvider.bookings.length,
scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
width: 150,
child: ExpansionTile(
title: Text(
index.toString() +
'. ' +
bookingsProvider
.bookings[index].futsal.customUser.name !=
null
? bookingsProvider
.bookings[index].futsal.customUser.name
: "",
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
children: <Widget>[
ListTile(
title: Text(
bookingsProvider.bookings[index].status,
),
subtitle: Text(
'For ' + bookingsProvider.bookings[index].bookedFor,
),
dense: true,
),
],
),
),
);
},
),
],
);
}
}
}
EditProfileScreen():
class EditProfileScreen extends StatefulWidget {
final CustomUser user;
EditProfileScreen({this.user});
#override
_EditProfileScreenState createState() => new _EditProfileScreenState();
}
class _EditProfileScreenState extends State<EditProfileScreen> {
final scaffoldKey = new GlobalKey<ScaffoldState>();
final formKey = new GlobalKey<FormState>();
String _name;
String _address;
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
void _submit() {
final form = formKey.currentState;
if (form.validate()) {
form.save();
performUpdate();
}
}
void performUpdate() async {
Map data = {
'name': _name,
'address': _address,
};
var url =MY_URL;
var response = await http.post(url,
headers: {"Accept": "application/json"}, body: data);
print(response.body);
if (response.statusCode == 200) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => LandingScreen(index: 2,)));
}
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return new SafeArea(
child: Scaffold(
key: scaffoldKey,
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
// automaticallyImplyLeading: false,
leading: BackButton(
color: kPrimaryColor,
),
elevation: 0,
backgroundColor: kPrimaryLightColor,
title: Text(
widget.user.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: kPrimaryColor,
),
),
),
body: new Container(
height: size.height,
width: double.infinity,
padding: const EdgeInsets.all(30.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Edit Details",
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: size.height * 0.03),
new Form(
key: formKey,
child: new Column(
children: <Widget>[
new TextFieldContainer(
child: new TextFormField(
controller:
TextEditingController(text: widget.user.name),
decoration: new InputDecoration(
labelText: "Name",
icon: Icon(
Icons.person,
color: kPrimaryColor,
),
border: InputBorder.none,
),
validator: (val) =>
val.isEmpty ? 'Please enter name' : null,
onSaved: (val) => _name = val,
),
),
new TextFieldContainer(
child: new TextFormField(
controller: TextEditingController(
text: widget.user.address != null
? widget.user.address
: ''),
decoration: new InputDecoration(
labelText: "Address",
icon: Icon(
Icons.email,
color: kPrimaryColor,
),
border: InputBorder.none,
),
validator: (val) =>
val.isEmpty ? 'Please enter your address' : null,
onSaved: (val) => _address = val,
),
),
RoundedButton(
text: "Update",
press: _submit,
),
],
),
),
],
),
),
),
),
);
}
}
As you are using Provider, you can do something like this.
#override
void initState() {
bloc = Provider.of<MyDealsBLOC>(context, listen: false);
if (!bloc.isLoaded) {
Future.delayed(
Duration.zero,
() => Provider.of<MyDealsBLOC>(context, listen: false).loadData(),
);
bloc.isLoaded = true;
print('IsLoaded: ${bloc.isLoaded}');
}
super.initState();
}
What I did is that I use a boolean isLoaded in my bloc to check whether data has been loaded once or not. I beleive you can do the same as well.
If you are trying to prevent a network image from keep downloading and loading you need to use cached_network_image to cache the image and it won't download again. Just put in the image download URL and the package will do the rest.

How can I show/hide menu items in Nav Drawer and enable/disable overflow menu items in App Bar programatically in Flutter?

I have been trying to do what the title says but have found no information on the web at all. In regular Android code, this is as simple as finding the ViewID of the drawer/toolbar, getting the menu item, and either calling .setEnabled() or .setVisible() on the menu item. How can I do this in Flutter? Basically when a certain url is loaded in webView, I want to either enable/disable or show/hide programatically (in the onLoadStart and onLoadFinished methods for webview_flutter). For reference, my scaffold:
return new Scaffold(
appBar: new AppBar(
title: new Text(appBarTitle),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.refresh,
color: Colors.white,
),
onPressed: () {
webView.reload();
},
),
PopupMenuButton<Choice> ( //showchoice??
onSelected: _select,
itemBuilder: (BuildContext context) {
return choices.map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Text(choice.title),
);
}).toList();
},
),
],
),
drawer: Drawer(
child: ListView(
children: <Widget>[
new DrawerHeader(
child: Column (
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget> [
Text(
'HLS Grades',
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
Text(
'Canvas Online Grading System',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
]),
decoration: BoxDecoration(
color: Colors.blue,
),
),
Divider(),
Text(
'Course Actions',
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
ListTile(
leading: Icon(MyFlutterApp.assignment),
title: Text('Assignments'),
onTap: () {
setState(() => _selectedDrawerIndex = 0);
_onSelectNavItem(0);
},
),
ListTile(
leading: Icon(MyFlutterApp.grades),
title: Text('Grades'),
onTap: () {
setState(() => _selectedDrawerIndex = 1);
_onSelectNavItem(1);
},
),
ListTile(
leading: Icon(MyFlutterApp.people),
title: Text('Users'),
onTap: () {
setState(() => _selectedDrawerIndex = 2);
_onSelectNavItem(2);
},
),
ListTile(
leading: Icon(MyFlutterApp.syllabus),
title: Text('Syllabus'),
onTap: () {
setState(() => _selectedDrawerIndex = 3);
_onSelectNavItem(3);
},
),
ListTile(
leading: Icon(MyFlutterApp.discussions),
title: Text('Discussions'),
onTap: () {
setState(() => _selectedDrawerIndex = 4);
_onSelectNavItem(4);
},
),
Divider(),
Text(
'App Actions',
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
ListTile(
leading: Icon(MyFlutterApp.logout),
title: Text('Logout'),
onTap: () {
_onSelectNavItem(5);
},
),
ListTile(
leading: Icon(MyFlutterApp.settings),
title: Text('Settings'),
onTap: () {
_onSelectNavItem(6);
},
),
],
),
),
And the code for my choice class:
class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const <Choice>[
const Choice(title: 'All Grading Periods'),
const Choice(title: 'Trimester 1'),
const Choice(title: 'Trimester 2'),
const Choice(title: 'Trimester 3'),
];
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.headline4;
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),
],
),
),
);
}
}
Any help would be appreciated!
For whoever is stumped by this and is looking for an answer, just wrap your ListTile in a
Visibility (
visible: _visible
child: ListTile(...)
);
and whenever you want to remove, just
setState(() {
_visible = false;
});
or whenever you want to show
setState(() {
_visible = true;
});

Flutter: How can I keep to my selected navigator when I'll go to a new screen and go back

I'm having a problem right now in my bottom navigation in flutter.
I have four navigation "Community, Feeds, Activity, Profile".
In my "Feeds" navigation I have a button named "View Profile" everytime I click that button it directs me to a new screen using
"Navigator.push(context, MaterialPageRoute())"
and I notice it auto generates a "<-" or "back arrow" icon on the appbar.
The problem is everytime I click that "back arrow", it redirects me to the first option on my navigation bar.
Not on the "Feeds" navigation.
Any tips how to fix this?
Here is my bottom navigation code:
_getPage(int page) {
switch (page) {
case 0:
return NewsFeed();
case 1:
return OrgAndNews();
case 2:
return MyActivity();
case 3:
return Profile();
}
}
int currentPage = 0;
void _onBottomNavBarTab(int index) {
setState(() {
currentPage = index;
});
}
return Scaffold(
body: Container(
child: _getPage(currentPage),
),
bottomNavigationBar: Container(
height: _height * .09,
child: BottomNavigationBar(
backgroundColor: Color(0xFFFFFFFF),
fixedColor: Color(0xFF121A21),
unselectedItemColor: Color(0xFF121A21),
currentIndex: currentPage,
onTap: _onBottomNavBarTab,
items: [
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.users),
title: Padding(
padding: const EdgeInsets.only(top: 3.0),
child: Text('Community', style: TextStyle(fontSize: ScreenUtil.getInstance().setSp(35),
fontWeight: FontWeight.w800),
),
),
),
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.newspaper),
title: Padding(
padding: const EdgeInsets.only(top: 3.0),
child: Center(
child: Text('Feeds', style: TextStyle(fontSize: ScreenUtil.getInstance().setSp(35),
fontWeight: FontWeight.w800),),
),
),
),
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.listUl),
title: Padding(
padding: const EdgeInsets.only(top: 3.0),
child: Text('My Activity', style: TextStyle(fontSize: ScreenUtil.getInstance().setSp(35),
fontWeight: FontWeight.w800),),
),
),
BottomNavigationBarItem(
icon: Icon(FontAwesomeIcons.userAlt),
title: Padding(
padding: const EdgeInsets.only(top: 3.0),
child: Text('Profile', style: TextStyle(fontSize: ScreenUtil.getInstance().setSp(35),
fontWeight: FontWeight.w800),),
),
),
],
),
),
);
My code for the page when you click the "View Profile":
class OrgProfile extends StatefulWidget {
OrgProfile(this.orgName) : super();
final String orgName;
#override
_OrgProfileState createState() => _OrgProfileState();
}
class _OrgProfileState extends State<OrgProfile> {
#override
final db = Firestore.instance;
Container buildItem(DocumentSnapshot doc) {
return Container(
child: Column(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: CircleAvatar(
radius: 70,
),
),
),
Text(
'${doc.data['Email']}',
style: TextStyle(color: Colors.black),
)
],
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.orgName),
),
body: StreamBuilder<QuerySnapshot>(
stream: db
.collection('USERS')
.where('Name of Organization', isEqualTo: widget.orgName)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data.documents
.map((doc) => buildItem(doc))
.toList());
} else {
return SizedBox();
}
}),
);
}
}
My code when i click the "View Profile" button:
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => new
OrgProfile(
doc.data['Name of Organization'])));
},
My feeds UI:
My View Profile UI:
Have you used MaterialPage Route With Builder Like This?
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => new MyToDoThunder(),
),
)
Homepage Code :-
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
//
return new HomePageState();
}
}
class HomePageState extends State<HomePage> {
var db = DatabaseHelper();
int _selectedIndex = 0;
List<bool> textColorChange = [true, false, false, false];
final _widgetOptions = [
StatusPageRedux(),
RequestPage(),
NotificationPage(),
DashboardPage(),
];
_bottomNavigationView() {
return new Theme(
isMaterialAppTheme: true,
data: Theme.of(context)
.copyWith(canvasColor: Theme.of(context).primaryColor),
child: new BottomNavigationBar(
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
currentIndex: _selectedIndex,
fixedColor: Colors.white,
items: [
new BottomNavigationBarItem(
activeIcon: ThunderSvgIcons(
path: 'assets/icons/Status.svg', height: 20.0, color: Colors.white),
icon: ThunderSvgIcons(
path: 'assets/icons/Status.svg', height: 20.0, color: Colors.white30),
title: new Text(
'Status',
style: TextStyle(
color: textColorChange[0] ? Colors.white : Colors.white30),
),
),
new BottomNavigationBarItem(
title: new Text(
'Requests',
style: TextStyle(
color: textColorChange[1] ? Colors.white : Colors.white30),
),
activeIcon: ThunderSvgIcons(
path: 'assets/icons/Requests.svg', height: 20.0, color: Colors.white),
icon: ThunderSvgIcons(
path: 'assets/icons/Requests.svg',
height: 20.0,
color: Colors.white30),
),
new BottomNavigationBarItem(
activeIcon: ThunderSvgIcons(
path: 'assets/icons/Notifications.svg',
height: 20.0,
color: Colors.white),
icon: ThunderSvgIcons(
path: 'assets/icons/Notifications.svg',
height: 20.0,
color: Colors.white30),
title: new Text(
'Notifications',
style: TextStyle(
color: textColorChange[2] ? Colors.white : Colors.white30),
),
),
new BottomNavigationBarItem(
activeIcon: ThunderSvgIcons(
path: 'assets/icons/dashboard.svg',
height: 20.0,
color: Colors.white),
icon: ThunderSvgIcons(
path: 'assets/icons/dashboard.svg',
height: 20.0,
color: Colors.white30),
title: new Text(
'Dashboard',
style: TextStyle(
color: textColorChange[3] ? Colors.white : Colors.white30),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(child: _widgetOptions.elementAt(_selectedIndex)),
bottomNavigationBar: _bottomNavigationView(),
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
for (int i = 0; i < textColorChange.length; i++) {
if (index == i) {
textColorChange[i] = true;
} else {
textColorChange[i] = false;
}
}
});
}
}
You will have to add your way back to the stack.
Try the below appbar in you 'tuloung duloung' title page, it should do the trick.
Note if your homescreen has tabs its advised to pass the index of the page you want to reach on exiting 'tuloung duloung'.
Let me know if it helps.
AppBar(
backgroundColor: Colors.transparent,
centerTitle: false,
brightness: Brightness.dark,
title: Container(
width: 150,
child: Row(
children:[
IconButton(icon:Icons.back_arrow,
onpressed:() =>
Navigator.pushReplacementNamed(context, '/Your Home_Screen');
),
Text('tuloung duloung',
style: TextStyle(
fontWeight: FontWeight.w400,
color: theme.primaryColor,
)),
]
),
),
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: theme.primaryColor,
),
actions:[ Container(
width: 150,
child: FlatButton.icon(
label: Text('Done'),
icon: Icon(Icons.check_circle),
onPressed: () => {
setState(() {
takingsnap = true;
_captureImage();
})
}),
),
]
),