How to save data locally using shared preferences - flutter

i want save data locally in device, when i will terminate my app and when i will reopen i want my previous data to be stored locally.so how i can save that using shared preferences in flutter
here my home page where i set value:
import 'package:flutter/material.dart';
import 'package:list_ex/product.dart';
import 'package:sizer/sizer.dart';
import 'package:list_ex/info.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'shared_pref.dart';
class Myhome extends StatefulWidget {
const Myhome({Key? key}) : super(key: key);
#override
State<Myhome> createState() => _MyhomeState();
}
class _MyhomeState extends State<Myhome> {
List <Data> productdata = [];
final myController = TextEditingController();
TextEditingController productController = TextEditingController();
TextEditingController prizeController = TextEditingController();
late SharedPreferences sharedPreferences;
#override
void initState() {
// TODO: implement initState
super.initState();
getprodata();
getpridata();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Products',
style: TextStyle(
fontSize: 30.0,
),
),
centerTitle: true,
backgroundColor: Colors.grey[800],
actions: [
IconButton(onPressed: () {
showDialog(context: context, builder: (context) =>
Dialog(
child: SizedBox(
height: 200,
width: 200,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(
filled: true,
labelText: 'product',
icon: Icon(Icons.star),
),
controller: productController,
validator: (value){
if(value == null || value.isEmpty){
return 'Enter product name';
}
return null;
},
),
Divider(
height: 20.0,
color: Colors.grey[800],
),
///Text Field
TextFormField(
decoration: InputDecoration(
filled: true,
labelText: 'price',
icon: Icon(Icons.star),
),
keyboardType: TextInputType.number,
controller: prizeController,
),
ElevatedButton(onPressed: () {
if (productController.text.isEmpty && prizeController.text.isEmpty){
const AlertDialog(
title: Text('Enter Value'),
);
} else{
setState(() {
setprodata(productController.text);
setpridata(prizeController.text);
productdata.add(Data(productController.text, prizeController.text));
productController.text = "";
prizeController.text = "";
Navigator.of(context).pop();
});
}
}, child:
const Text('Submit')),
],
),
),
),
);
}, icon: Icon(Icons.add))
],
),
///app Drawer
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
UserAccountsDrawerHeader(
decoration: BoxDecoration(
color: Colors.black45
),
accountName: Text('Raj'),
accountEmail: Text('abc123#gmail.com'),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.orange,
child:
Text('R', style:
TextStyle(fontSize: 40),),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: Icon(Icons.contact_mail),
title: Text('Contact Us'),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
///Body of the app
body: ListView.builder(
itemCount: productdata.length,
itemBuilder: (BuildContext context, int index) {
return
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: ListTile(
tileColor: Colors.cyan,
leading: Icon(Icons.star),
trailing: IconButton(onPressed: () {
showDialog(context: context, builder: (context) =>
AlertDialog(
title: Text('Delte this?'),
content: Text('Are you sure?'),
actions: <Widget>[
TextButton(onPressed: () {
Navigator.pop(context);
}, child:
Text('Cancel')),
TextButton(onPressed: () {
setState(() {
productdata.remove(productdata[index]);
Navigator.pop(context);
});
}, child:
const Text('Delete', style:
TextStyle(
color: Colors.black87,
fontSize: 16,
),))
],
));
}, icon: Icon(Icons.delete)),
title: Text(productdata[index].product!,
style:
TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
),),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Info(value: productdata[index])));
},
)),
],
);
}),
);
}
}
i tried using shared preferences but i am not geting any value.
here my info page where i want to get values:
import 'package:flutter/material.dart';
import 'package:list_ex/home.dart';
import 'package:list_ex/product.dart';
import 'package:list_ex/shared_pref.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Info extends StatelessWidget {
final Data value;
var pro;
var pri;
#override
void initState() {
getprodata();
getpridata();
}
Info({Key? key, required this.value}) : super(key: key);
#override
Widget build(BuildContext context) {
var pridata;
var prodata;
return Scaffold(
appBar: AppBar(
title: Text('Product Info'),
centerTitle: true,
backgroundColor: Colors.grey[800],
),
body: Center(
child: Card(
color: Colors.cyan,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
// ignore: prefer_interpolation_to_compose_strings
title: Text('Product Name:' + value.product!, style:
TextStyle(
fontSize: 20,
),),
subtitle: Text('Price:' + value.prize!, style:
TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold
),),
),
],
),
),
)
);
}
}

first import shared_preferences library into your project.
make instance of preference.
final prefs = await SharedPreferences.getInstance();
now write or store data into preference
counter is for Key and 10 is value
await prefs.setInt('counter', 10);
what ever key you will give here it will store data into that key only through this key you can get you data.
you can store any type of data like for int type you have to use setInt for String type use setString() and so on..
now you can get this data through get
final int? counter = prefs.getInt('counter');
in getInt() just pass key that you want to get data.
and use only one instance in every screen of you project to write and get data.
for more information see https://pub.dev/packages/shared_preferences

Related

Trying to a search functioning to my Flutter notes app

Im trying to add a search function to my notes app but my gridview doesnt seem to work.
I have revised my code different times for the Gridview but same result. NoteReaderScreen(noteData: note.data()) is always red. any help is accepted thanks
home_screen.dart:
import 'package:BetterNotes/screens/note_editor.dart';
import 'package:BetterNotes/screens/note_reader.dart';
import 'package:BetterNotes/screens/settings.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../style/app_style.dart';
class HomeScreen extends StatefulWidget {
final Color backgroundColor;
const HomeScreen({Key? key, required this.backgroundColor}) : super(key: key);
#override
_HomeScreenState createState() =>
_HomeScreenState(backgroundColor: AppStyle.mainColor);
}
class _HomeScreenState extends State<HomeScreen> {
final Color backgroundColor;
final TextEditingController _searchController = TextEditingController();
_HomeScreenState({required this.backgroundColor});
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppStyle.mainColor,
appBar: AppBar(
elevation: 0,
title: const Text('Better Notes'),
centerTitle: true,
backgroundColor: AppStyle.mainColor,
actions: [
IconButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen()));
},
icon: const Icon(Icons.settings))
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
"Your recent Notes",
style: GoogleFonts.roboto(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextField(
controller: _searchController,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.search,
color: Colors.white,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
],
),
const SizedBox(
height: 20,
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
// Set the stream to either all documents from the 'notes' collection,
// or a filtered subset of the documents based on the user's search query.
stream: _searchController.text.isEmpty
? FirebaseFirestore.instance.collection('notes').snapshots()
: FirebaseFirestore.instance
.collection('notes')
.where('note_title', isEqualTo: _searchController.text)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
// Show a loading indicator while the stream is waiting for data.
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
// Get the list of documents from the snapshot.
final List<DocumentSnapshot> notes = snapshot.data!.docs;
// Filter the list of documents based on the search query.
final List<DocumentSnapshot> matchingNotes = notes
.where((note) => note['note_title']
.toString()
.toLowerCase()
.contains(_searchController.text.toLowerCase()))
.toList();
// Map the list of documents to a list of NotesCard widgets.
// Pass the data from each document to the NotesCard widgets as arguments.
return GridView.count(
crossAxisCount: 2,
children: matchingNotes
.map((note) => NotesCard(
noteData: note.data(),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NoteReaderScreen(
noteData: note.data(),
),
),
);
},
))
.toList(),
);
}
return const Center(
child: Text("No notes found"),
);
},
),
),
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const NoteEditorScreen()));
},
label: const Text("Add"),
icon: const Icon(
Icons.add,
color: Colors.white,
),
backgroundColor: Colors.purple,
),
);
}
}
Tried changing it different times but same result
You are not setting state when textfield change.
You can change that widget with this.
Expanded(
child: TextField(
onChanged: (value){setState(() {});},
controller: _searchController,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(
prefixIcon: const Icon(
Icons.search,
color: Colors.white,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
],
),

Boolean always turn into 'False', spontaneously

I'm building 'What to Do list' app,
I made my own class Bucket(String, String, bool).
3rd property bool plays a role in checking if this task has done.
I designed if bool is false, unfulfilled task get fontweight.bold and icon is shown red, and if bool is true, fulfilled task get fontweight.normal and icons is shown green.
But, as soon as bool get 'true', it turns in to 'false', spontaneously.
No other codes are designed to get bool changed.
I don't understand why :(
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatefulWidget {
Homepage({Key? key}) : super(key: key);
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WhattoDo List')),
floatingActionButton: IconButton(
onPressed: () async {
Bucket newbucket = await Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PopUp()));
setState(() {
bucketlist.add(newbucket);
});
},
icon: Icon(Icons.add)),
body: bucketlist.isEmpty
? Center(child: Text('List is empty'))
: ListView.builder(
itemCount: bucketlist.length,
itemBuilder: (BuildContext context, int index) {
bool donecheck = bucketlist[index].isDone;
print(donecheck);
return Container(
margin: EdgeInsets.only(top: 5),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.teal[200],
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(
bucketlist[index].whattodo,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
decoration: donecheck
? TextDecoration.lineThrough
: TextDecoration.none),
),
subtitle: Text(
bucketlist[index].deadline,
style: TextStyle(fontSize: 15),
),
trailing: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Wanna remove?"),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
setState(() {
bucketlist.removeAt(index);
});
},
child: Text(
"OK",
style: TextStyle(color: Colors.pink),
),
),
],
);
},
);
},
icon: Icon(Icons.delete)),
leading: IconButton(
onPressed: () {
setState(() {
donecheck = !donecheck;
print(donecheck);
});
},
icon: Icon(
Icons.check,
color: donecheck ? Colors.green : Colors.red,
)),
));
}));
}
}
class Bucket {
String whattodo;
String deadline;
bool isDone;
Bucket(this.whattodo, this.deadline, this.isDone);
}
List<Bucket> bucketlist = [];
class PopUp extends StatefulWidget {
const PopUp({Key? key}) : super(key: key);
#override
State<PopUp> createState() => _PopUpState();
}
class _PopUpState extends State<PopUp> {
TextEditingController textController = TextEditingController();
TextEditingController textController2 = TextEditingController();
String? errorshowingtext;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), color: Colors.blue[200]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 150,
),
Text(
'Write down your task',
style: TextStyle(
fontSize: 25,
color: Colors.black,
fontWeight: FontWeight.normal),
),
SizedBox(
height: 25,
),
TextField(
autofocus: true,
decoration: InputDecoration(
hintText: 'What to Do', errorText: errorshowingtext),
style: TextStyle(fontSize: 22),
controller: textController,
),
TextField(
autofocus: true,
decoration: InputDecoration(
hintText: 'Deadline', errorText: errorshowingtext),
style: TextStyle(fontSize: 22),
controller: textController2,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('OK'),
onPressed: () {
String? job = textController.text;
String? line = textController2.text;
Bucket newbucket = Bucket(job, line, false);
if (job.isNotEmpty && line.isNotEmpty) {
setState(() {
Navigator.pop(context, newbucket);
});
} else {
setState(() {
errorshowingtext = 'contents missing';
});
}
},
),
SizedBox(
width: 150,
),
ElevatedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
],
)
],
),
),
);
}
}
You already have isDone to check the state. Your current method doesn't update the item check status, I prefer this way
class Bucket {
final String whattodo;
final String deadline;
final bool isDone;
Bucket({
required this.whattodo,
required this.deadline,
required this.isDone,
});
Bucket copyWith({
String? whattodo,
String? deadline,
bool? isDone,
}) {
return Bucket(
whattodo: whattodo ?? this.whattodo,
deadline: deadline ?? this.deadline,
isDone: isDone ?? this.isDone,
);
}
}
And the widget
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WhattoDo List')),
floatingActionButton: IconButton(
onPressed: () async {
Bucket newbucket = await Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => PopUp()));
setState(() {
bucketlist.add(newbucket);
});
},
icon: Icon(Icons.add)),
body: bucketlist.isEmpty
? Center(child: Text('List is empty'))
: ListView.builder(
itemCount: bucketlist.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(top: 5),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.teal[200],
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(
bucketlist[index].whattodo,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
decoration: bucketlist[index].isDone
? TextDecoration.lineThrough
: TextDecoration.none),
),
subtitle: Text(
bucketlist[index].deadline,
style: TextStyle(fontSize: 15),
),
trailing: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Wanna remove?"),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Cancel"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
setState(() {
bucketlist.removeAt(index);
});
},
child: Text(
"OK",
style: TextStyle(color: Colors.pink),
),
),
],
);
},
);
},
icon: Icon(Icons.delete)),
leading: IconButton(
onPressed: () {
setState(() {
bucketlist[index] = bucketlist[index]
.copyWith(isDone: !bucketlist[index].isDone);
});
},
icon: Icon(
Icons.check,
color: bucketlist[index].isDone
? Colors.green
: Colors.red,
)),
));
},
),
);
}
}
you have to update the list todo value. not the Widget of Listile value.
change this
leading: IconButton(
onPressed: () {
setState(() {
// your code: donecheck = !donecheck;
bucketlist[index].isDone = !donecheck; // change to this
print(donecheck);
});
},

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 to fix RangeError after deleting the data from Firebase and removing it from List Flutter?

I am getting RangeError when I delete the data from the firebase realtime database using flutter application.
Here is the error
I/flutter (10577): The following RangeError was thrown building:
I/flutter (10577): RangeError (index): Invalid value: Valid value range is empty: 0
And the error is appearing after this function executes
_deleteTask(int index)
Following is the code
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import '../Task.dart';
import 'package:intl/intl.dart';
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final FirebaseDatabase database = new FirebaseDatabase();
DatabaseReference databaseReference;
List<Task> tasks = List();
Task task;
#override
void initState() {
super.initState();
databaseReference = database.reference().child("Notes");
task = Task("", "");
databaseReference.onChildAdded.listen(_childAdded);
databaseReference.onChildChanged.listen(_childChanged);
//databaseReference.onChildRemoved.listen(_childDlt);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black54,
title: Text(
"Todo",
style: TextStyle(color: Colors.white),
),
actions: <Widget>[
IconButton(
icon:Icon(Icons.done_all),
onPressed: (){
setState(() {
databaseReference.remove();
});
},
)
],
),
body: Container(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Flexible(
child: FirebaseAnimatedList(
query: databaseReference,
itemBuilder: (context, DataSnapshot snapshot,
Animation<double> animation, int indexxx) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
color: Colors.white30,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
onTap: ()=>_editTask(indexxx),
title: Text("${tasks[indexxx].task}",style: TextStyle(color: Colors.white,fontSize: 18,fontWeight: FontWeight.w600),),
subtitle: Text("Created on: ${tasks[indexxx].date}",style: TextStyle(color: Colors.white,fontStyle: FontStyle.italic)),
trailing: IconButton(
icon: Icon(Icons.remove_circle,color: Colors.red,),
onPressed: ()=>_deleteTask(indexxx),
)
),
),
);
}),
)
],
),
),
),
floatingActionButtonLocation:FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add,color: Colors.blue,size: 40,),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0)
),
onPressed: ()=>_newTask(),
//label: Text("New Task"),
),
);
}
_newTask(){
TextEditingController textEditingController=TextEditingController();
var alert=AlertDialog(
title: Text("Add New Task"),
content: TextField(
controller: textEditingController,
decoration: InputDecoration(
labelText: "Enter Text"
//hintText: "Enter Text",
),
autofocus: true,
),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: (){
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Add"),
onPressed: (){
String text=textEditingController.text;
DateTime now = DateTime.now();
String formattedDate = DateFormat('MMM d,y').format(now);
task.date=formattedDate;
task.task=text;
databaseReference.push().set(task.toJson());
Navigator.of(context).pop();
},
),
],
);
showDialog(context: context,builder:(context){
return alert;
});
}
_editTask(int index) {
TextEditingController textEditingController=TextEditingController();
textEditingController.text=tasks.elementAt(index).task;
var alert=AlertDialog(
title: Text("Edit Task"),
content: TextField(
controller: textEditingController,
decoration: InputDecoration(
labelText: "Enter Text",
//hintText: "Enter Text",
),
autofocus: true,
),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: (){
Navigator.of(context).pop();
},
),
FlatButton(
child: Text("Update"),
onPressed: (){
String text=textEditingController.text;
DateTime now = DateTime.now();
String formattedDate = DateFormat('MMM d,y').format(now);
task.date=formattedDate;
task.task=text;
databaseReference.child(tasks.elementAt(index).key).update(task.toJson());
setState(() {
Navigator.of(context).pop();
});
},
),
],
);
showDialog(context: context,builder:(context){
return alert;
});
}
_deleteTask(int index) {
setState(() {
databaseReference.child(tasks.elementAt(index).key).remove();
tasks.removeAt(index);
});
}
void _childAdded(Event event) {
setState(() {
tasks.add(Task.fromSnapshot(event.snapshot));
});
}
void _childChanged(Event event) {
var oldTask=tasks.singleWhere((entry){
return event.snapshot.key==entry.key;
});
setState(() {
tasks[tasks.indexOf(oldTask)]=Task.fromSnapshot(event.snapshot);
});
}
}