this my screen where i fetch data from API using FutureBuilder . I would like to save data getting from server on device and reuse it when i need . I wouldn't like each time fetch data from the server when i open the screen .
My screen :
and this my code :
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SafeArea(
/* minimum: const EdgeInsets.only(
top: 20.0, right: 5.0, left: 5.0, bottom: 10.0),*/
child: Center(
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Color(0xFFF6F7F8),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: FutureBuilder(
future: future,
// boxApi.getUser(),
builder: (context, snapshot) {
// ignore: missing_return
print(snapshot.data);
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('no connection');
case ConnectionState.active:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
break;
case ConnectionState.done:
if (snapshot.hasError) {
return Text('error');
} else if (snapshot.hasData) {
// String price = snapshot.data['body'].;
// print("${snapshot.data}");
return Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Container(
padding: EdgeInsets.only(top: 16),
width: MediaQuery.of(context).size.width,
height:
MediaQuery.of(context).size.height / 4,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.white60,
blurRadius: 15.0,
offset: Offset(0.0, 0.75))
],
gradient: LinearGradient(
begin: Alignment(0.5, 0.85),
end: Alignment(0.48, -1.08),
colors: [
const Color(0xFF0B0C3A),
const Color(0xFF010611),
],
stops: [
0.0,
0.5,
],
),
//color: blue,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(32),
bottomLeft: Radius.circular(32))),
child: Column(
children: [
Row(
children: [
SizedBox(
width: 30,
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"${snapshot.data.name}",
style: TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight:
FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
"${snapshot.data.phone}",
style: TextStyle(
color: Colors.white60,
fontSize: 18,
//fontWeight: FontWeight.w300
),
),
])
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
margin: EdgeInsets.symmetric(
vertical: 10),
width: size.width * 0.4,
child: ElevatedButton(
onPressed: () {
if (_nameController.text ==
"" &&
_emailController.text ==
"" &&
_adressController
.text ==
"") {
setState(() =>
isButtonDisabled =
true);
} else {
editUserProfile();
}
},
// editUserProfile();
child: Text('Enregistrer'),
style:
ElevatedButton.styleFrom(
primary: Colors.transparent,
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
20),
side: BorderSide(
color: Colors
.white)),
),
)),
SizedBox(
width: 20,
),
],
)
],
),
),
Container(
height: MediaQuery.of(context).size.height /
1.5,
// padding: EdgeInsets.only(
// top: 32,
// ),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Container(
width: size.width * 0.94,
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(
left: 10,
right: 10,
bottom: 20,
top: 20),
child: Column(
mainAxisAlignment:
MainAxisAlignment
.start,
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Votre nom :',
style: TextStyle(
color: Color(
0xFF4053FCF),
fontSize: 16,
fontWeight:
FontWeight
.w600),
),
IconButton(
icon: Icon(
CommunityMaterialIcons
.pencil,
color: Colors
.grey,
),
onPressed: () {
myFocusNode
.requestFocus();
setState(() {
enableup =
true;
});
})
],
),
TextFormField(
controller:
_nameController,
enabled: enableup,
focusNode:
myFocusNode,
enableInteractiveSelection:
false,
keyboardType:
TextInputType
.text,
decoration: InputDecoration(
hintText:
"${snapshot.data.name}",
hintStyle: TextStyle(
color: Colors
.grey,
fontSize:
14.0)),
),
Future<User> getUser() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
String token = localStorage.getString('access_token');
// print(token);
await checkInternet();
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
};
var url = Uri.parse(ApiUtil.GET_USER);
var response = await http.get(url, headers: headers);
switch (response.statusCode) {
case 200:
var body = jsonDecode(response.body);
// print(body);
User users = User.fromJson(body);
// inspect(users);
// print(users);
return users;
break;
case 404:
throw ResourceNotFound('user');
break;
case 301:
case 302:
case 303:
throw RedirectionFound();
break;
default:
return null;
break;
}
}
How i can save data locally and reuse it without connect to the server ?
Update :
response from api :
{
"id": 53,
"activeboxe_id": 35,
"username": "hamid hamid",
"name": "Hamid Ansari",
"email": "hamid#gmail.com",
"adress": "Germany",
"phone": "21625147147",
"email_verified_at": null,
"created_at": "2021-04-28T10:52:31.000000Z",
"updated_at": "2021-05-28T09:35:17.000000Z",
"role": "user"
}
You could use a SQLite DB.
Consider the docs.
There you can find an example.
Consider using Hive or Object_Box. The are no SQL dbs, and they are very fast and performant.
If you are using bloc as your state management, HydratedBloc is also available. Which is what I use personally
Related
I have trouble displaying data, all data is received correctly from Get method, but I can not handle it to show values.
in the getDataLadder successfully received data in response.body and i can see result with print('Response Body : ${response.body}');
with this section i store data in stringResponse value , but i can't show json data in this section:
Widget getBody() {
var size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: size.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10)),
color: Colors.white),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Container(
height: 1,
decoration:
BoxDecoration(color: Colors.grey.withOpacity(0.2)),
),
),
SizedBox(
width: 10,
),
Text(
stringResponse.toString(),
style: TextStyle(color: Colors.black54),
),
SizedBox(
width: 10,
),
Flexible(
child: Container(
height: 1,
decoration:
BoxDecoration(color: Colors.grey.withOpacity(0.2)),
),
),
],
),
SizedBox(
height: 15,
),
Column(
children: List.generate(nardeban_data.length, (index) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(right: 5, left: 5),
child: Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage("assets/images/bg.png"),
fit: BoxFit.fill,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: 90,
// width: (size.width - 20) * 0.68,
child: Row(
children: [
SizedBox(width: 20), // give it width
Container(
width: 40,
decoration: BoxDecoration(
color: Colors.blue,
border:
Border.all(color: Colors.blue),
borderRadius:
BorderRadius.circular(40.0)),
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("14",
style: TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.bold,
color: Colors.white)))),
SizedBox(
width: 10,
),
Container(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
nardeban_data[index]['name'],
style: TextStyle(
fontSize: 14,
color: Colors.black54,
fontWeight: FontWeight.w400),
overflow: TextOverflow.ellipsis,
),
],
),
)
],
),
),
Container(
width: (size.width - 120) * 0.32,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.blue)),
child: Padding(
padding: const EdgeInsets.only(
right: 10,
bottom: 4,
left: 10,
top: 4),
child: Row(
children: [
SizedBox(
width: 25,
height: 25,
child: TextField(
textAlign: TextAlign.center,
keyboardType:
TextInputType.number,
decoration: InputDecoration(
hintText: '4%',
border: InputBorder.none,
contentPadding: EdgeInsets.only(
bottom: 8,
top: 3,
),
),
),
)
],
),
),
),
// Icon(
// Icons.notifications,
// size: 22,
// color: Colors.blue.withOpacity(0.7),
// )
],
),
)
],
),
)),
],
);
})),
],
),
),
);
}
this is my json style when i get from api:
{
"pellekan": [
{
"name": "1",
"form": null,
"to": "1000000",
"percent": 1
},
{
"name": "2",
"form": "1000000",
"to": 1100000,
"percent": 2.89
},
{
"name": "3",
"form": "1000000",
"to": 1200000,
"percent": 4.79
},
]
}
also this is my class and i difine stringResponse value out of that
var stringResponse = [];
class ResultLadder extends StatefulWidget implements PreferredSizeWidget {
#override
Size get preferredSize => const Size.fromHeight(100);
const ResultLadder({Key? key}) : super(key: key);
#override
_ResultLadderState createState() => _ResultLadderState();
}
Future<String> getToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getString('login')!;
}
class _ResultLadderState extends State<ResultLadder> {
String token = "";
#override
void initState() {
// getInfo();
getDataLadder();
super.initState();
}
generally, for example how i can show name in json data instead of "14" , in this below:code:
child: Text("14",
style: TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.bold,
color: Colors.white))
this is getDataLadder Code :
Future getDataLadder() async {
print('bye');
String token = await getToken();
var response = await http.get(
Uri.parse('https://mylocalapiurl.com/backend/api/v1/pellekan/main/active'),
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
print('Token : ${token}');
print('Response Body : ${response.body}');
if (response.statusCode == 200) {
setState(() {
stringResponse = json.decode(response.body);
});
}
}
nardeban ladder is one file in data folder and i add this values for show data demo (this is working):
const List nardeban_data = [
{
"img":
"https://images.unsplash.com/image.jpg",
"name": "mark wiliams",
"takhfif": "description item"
},
{
"img":
"https://images.unsplash.com/photo-1610859923380-c8e5a13165d1?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80",
"name": "joe mark",
"takhfif": "description item"
},
];```
So now use your stringResponse since it has already been decoded into JSON, I'd create a small PODO (Plain Ol' Dart Object) to map my JSON to the data you're receiving, as in:
class NardebanData {
String? name;
String? form;
String? to;
double? percent;
NardebanData({ this.name, this.form, this.to, this.percent });
factory NardebanData.fromJson(Map<String, dynamic> json) {
return NardebanData(
name: json['name'],
form: json['form'],
to: json['to'],
percent: json['percent']
);
}
}
Inside your setState() call, then map it as such:
setState(() {
stringResponse = json.decode(response.body);
nardeban_data = (stringResponse['pellekan'] as List<dynamic>).map((d) => NardebanData.fromJson(d)).toList();
});
So make your nardeban_data not a const anymore, but a List of NardebanData objects:
List<NardebanData> narbeban_data = [];
And when you consume it (inside of your List.generate) you could fetch the current object in the iteration, as in:
children: List.generate(nardeban_data.length, (index) {
// fetch the item in the collection by the index provided
NardebanData dataItem = nardeban_data[index];
// ALL YOUR WIDGET STRUCTURE HERE
// LIKE YOUR TEXT WIDGETS AS IN:
Text(
dataItem.name!, // pull the data out of the dataItem using the provided properties
style: TextStyle(
fontSize: 14,
color: Colors.black54,
fontWeight: FontWeight.w400),
overflow: TextOverflow.ellipsis,
),
}
Also make sure your JSON data coming from your API is consistent as far as the type of data in each field, otherwise the decoding will fail (i.e. make sure that name, form, to are Strings, and percent is a double).
See if that works for you.
I'm developing a job search app that scrapes data from Indeed using Python which is being sent back to my Flutter UI as JSON data. The JSON data is being received successfully, however, Im getting an error of Null check operator used on a null value. The error appears to be stemming from the _jobSearch widget.
The relevant error-causing widget was ListView lib/ui/home_page.dart:256
Exception caught by scheduler library
Null check operator used on a null value
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_job_portal/theme/colors.dart';
import 'package:flutter_job_portal/theme/images.dart';
import 'package:flutter_job_portal/ui/bottom_menu_bar.dart';
import 'package:flutter_job_portal/ui/job_detail_page.dart';
String job = ""; //user's response will be assigned to this variable
String final_response = "";
final _formkey = GlobalKey<FormState>(); //key created to interact with the form
//function to validate and save user form
Future<void> _savingData() async {
final validation = _formkey.currentState.validate();
if (!validation) {
return;
}
_formkey.currentState.save();
}
Future<List<Job>> _getJobs() async {
final url = 'http://127.0.0.1:5000/job';
final response1 = await http.post(Uri.parse(url), body: json.encode({'job': job}));
final response2 = await http.get(Uri.parse(url));
final decoded = json.decode(response2.body);
List<Job> jobs = [];
for (var i in decoded) {
Job job = Job(i['Title'], i['Company'], i['Location'], i['Salary']);
jobs.add(job);
}
return jobs;
}
class Job {
final String title;
final String company;
final String location;
final String salary;
Job(this.title, this.company, this.location, this.salary);
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
Widget _appBar(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(Images.user1),
),
Spacer(),
IconButton(
icon: Icon(Icons.notifications_none_rounded),
onPressed: () {},
)
],
),
);
}
Widget _header(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hello, Alex!",
style: TextStyle(
fontSize: 15,
color: KColors.subtitle,
fontWeight: FontWeight.w500,
)),
SizedBox(
height: 6,
),
Text("Swipe to find your future",
style: TextStyle(
fontSize: 20,
color: KColors.title,
fontWeight: FontWeight.bold)),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Container(
height: 45,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: KColors.lightGrey,
borderRadius: BorderRadius.circular(10)),
child: Form(
key: _formkey,
child: TextFormField(
decoration: InputDecoration(
hintText: 'Search job title or keywords',
),
onSaved: (value) {
job =
value; //getting data from the user form and assigning it to job
},
),
),
),
),
SizedBox(
width: 16,
),
Container(
decoration: BoxDecoration(
color: KColors.primary,
borderRadius: BorderRadius.circular(10),
),
height: 40,
child: IconButton(
color: KColors.primary,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () async {
_savingData();
_getJobs();
},
),
)
],
)
],
),
);
}
Widget _recommendedSection(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
margin: EdgeInsets.symmetric(vertical: 12),
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Recommended",
style: TextStyle(fontWeight: FontWeight.bold, color: KColors.title),
),
SizedBox(height: 10),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_recommendedJob(context,
company: "Google",
img: Images.google,
title: "UX Designer",
sub: "\$45,000 Remote",
isActive: true),
_recommendedJob(context,
company: "DropBox",
img: Images.dropbox,
title: "Research Assist",
sub: "\$45,000 Remote",
isActive: false)
],
),
),
],
),
);
}
Widget _recommendedJob(
BuildContext context, {
String img,
String company,
String title,
String sub,
bool isActive = false,
}) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
Navigator.push(context, JobDetailPage.getJobDetail());
},
child: AspectRatio(
aspectRatio: 1.3,
child: Container(
decoration: BoxDecoration(
color: isActive ? KColors.primary : Colors.white,
borderRadius: BorderRadius.circular(7),
),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: 40,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: isActive ? Colors.white : KColors.lightGrey,
borderRadius: BorderRadius.circular(7),
),
child: Image.asset(img),
),
SizedBox(height: 16),
Text(
company,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
SizedBox(height: 6),
Text(
title,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : KColors.title,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 6),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
],
),
),
),
),
);
}
Widget _jobSearch(BuildContext context) {
return new Container(
child: FutureBuilder(
future: _getJobs(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].location),
);
},
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: KColors.background,
bottomNavigationBar: BottomMenuBar(),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_appBar(context),
_header(context),
_recommendedSection(context),
_jobSearch(context)
],
),
),
),
),
);
}
}
I'm fetching data from server APIs, The data is being successfully fetched from the server but the issue is that when the data is provided to Listview it cant be shown. How can I show the data on Listview in a flutter/dart?
Following is the code for fetching data from server API's
List<String> jobTitles = [];
List officeNames = [ ];
List officeLocations = [ ];
List jobTypes = [ ];
Future getJobsData() async {
var response = await http
.get(Uri.https('hospitality92.com', 'api/jobsbycategory/All'));
Map<String, dynamic> map = json.decode(response.body);
List<dynamic> jobData = map["jobs"];
if (jobTitles.length != 0) {
officeNames.clear();
jobTitles.clear();
officeLocations.clear();
jobTypes.clear();
}
for (var i = 0; i < jobData.length; i++) {
jobTitles.add(jobData[i]["title"]);
officeNames.add(jobData[i]["company_name"]);
officeLocations.add(jobData[i]["company_name"]);
jobTypes.add(jobData[i]["type"]);
}
/* print(jobTitles);
print(officeNames);
print(officeLocations);
print(jobTypes);*/
}
Here is the design code that I wanted to show:
Widget listCard(jobTitle, officeName, location, jobType, onPress) {
return Container(
child: InkWell(
onTap: onPress,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xC000000), Color(0xC000000)])),
child: ListTile(
leading: CircleAvatar(
radius: 30,
child: Image.asset(
"assets/ic_login.png",
height: 28,
width: 28,
),
),
title: Text(
jobTitle,
style: TextStyle(
fontSize: 16,
color: Colors.lightBlue,
fontFamily: 'Montserrat',
fontWeight: FontWeight.w500),
),
subtitle: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
Icon(
Icons.home_outlined,
color: Colors.black,
size: 16,
),
Text(
officeName,
style: TextStyle(fontSize: 10),
),
],
),
SizedBox(
width: 10,
),
Row(
children: [
Icon(
Icons.location_pin,
color: Colors.blueGrey,
size: 16,
),
SizedBox(
width: 2,
),
Text(
location,
style: TextStyle(fontSize: 10),
),
],
),
SizedBox(
width: 10,
),
Row(
children: [
Container(
margin: const EdgeInsets.fromLTRB(0, 4, 0, 0),
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
Colors.lightBlueAccent,
Colors.lightBlueAccent
])),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
jobType,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),
],
)
//ElevatedButton(onPressed: () { }, child: Text("Full Time", style: TextStyle(fontSize: 10),),),
],
),
),
),
),
),
],
),
),
);
}
Here is the listview code
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: jobTitle.length,
itemBuilder: (context, index) {
return listCard(jobTitles[index], officeNames[index],
officeLocations[index], jobTypes[index], () {});
}),
If I provide the static data to list it will show on listview, but dynamic data is not being shown.
Try To below Code Your problem has been solved:
Create API Call Function
Future<List<dynamic>> getJobsData() async {
String url = 'https://hospitality92.com/api/jobsbycategory/All';
var response = await http.get(Uri.parse(url), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
});
return json.decode(response.body)['jobs'];
}
Write/Create your Widget :
Center(
child: FutureBuilder<List<dynamic>>(
future: getJobsData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
var title = snapshot.data[index]['title'];
var company = snapshot.data[index]['company_name'];
var skills = snapshot.data[index]['skills'];
var description = snapshot.data[index]['description'];
var positions = snapshot.data[index]['positions'];
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(
color: Colors.green.shade300,
),
borderRadius: BorderRadius.circular(15.0),
),
child: ListTile(
leading: Text(skills),
title: Text(title),
subtitle: Text(
company + '\n' + description,
),
trailing: Text(positions),
),
);
},
),
);
}
return CircularProgressIndicator();
},
),
),
Your Screen Look Like
I am trying to get some values saved in the SharedPreferences . I have a few textfields with hint text and bottom navigation. Text entered into the textfield will be saved to shared preferences. I created class named loadUserData(); to save data locally using SharedPreferences .
void initState() {
super.initState();
isButtonDisabled = false;
loadUserData();
myFocusNode = FocusNode();
telephoneNode = FocusNode();
adresseNode = FocusNode();
emailNode = FocusNode();
passwordNode = FocusNode();
// future = boxApi.getUser();
// future = loadUserData();
}
loadUserData() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var user = jsonDecode(localStorage.getString('user'));
if (user != null) {
setState(() {
name = user['name'];
phone = user['phone'];
adress = user['adress'];
email = user['email'];
});
}
}
After that , I tried to fetch data from SharedPreferences and display it on screen like that :
as you see the user can modify his name , adress... My problem is when the user modify some data ==> the data not updated until he login next time . I want the user can modify and update the new value at the same time ( don't need leave page) .
this my code ;
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return SafeArea(
/* minimum: const EdgeInsets.only(
top: 20.0, right: 5.0, left: 5.0, bottom: 10.0),*/
child: Center(
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Color(0xFFF6F7F8),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: FutureBuilder(
future: future,
// boxApi.getUser(),
//print(adress);
builder: (context, snapshot) {
// ignore: missing_return
// print(snapshot.data);
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('no connection');
case ConnectionState.active:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
break;
case ConnectionState.done:
if (snapshot.hasError) {
return Text('error');
} else if (snapshot.hasData) {
// String price = snapshot.data['body'].;
// print("${snapshot.data}");
return Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Container(
padding: EdgeInsets.only(top: 16),
width: MediaQuery.of(context).size.width,
height:
MediaQuery.of(context).size.height / 4,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.white60,
blurRadius: 15.0,
offset: Offset(0.0, 0.75))
],
gradient: LinearGradient(
begin: Alignment(0.5, 0.85),
end: Alignment(0.48, -1.08),
colors: [
const Color(0xFF0B0C3A),
const Color(0xFF010611),
],
stops: [
0.0,
0.5,
],
),
//color: blue,
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(32),
bottomLeft: Radius.circular(32))),
child: Column(
children: [
Row(
children: [
SizedBox(
width: 30,
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"$name",
style: TextStyle(
color: Colors.white,
fontSize: 25,
fontWeight:
FontWeight.bold),
),
SizedBox(
height: 10,
),
Text(
"$phone",
style: TextStyle(
color: Colors.white60,
fontSize: 18,
//fontWeight: FontWeight.w300
),
),
])
],
),
Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Container(
margin: EdgeInsets.symmetric(
vertical: 10),
width: size.width * 0.4,
child: ElevatedButton(
onPressed: () {
if (_nameController.text ==
"" &&
_emailController.text ==
"" &&
_adressController
.text ==
"") {
setState(() =>
isButtonDisabled =
true);
} else {
editUserProfile();
}
},
// editUserProfile();
child: Text('Enregistrer'),
style:
ElevatedButton.styleFrom(
primary: Colors.transparent,
shape:
RoundedRectangleBorder(
borderRadius:
BorderRadius
.circular(
20),
side: BorderSide(
color: Colors
.white)),
),
)),
SizedBox(
width: 20,
),
],
)
],
),
),
Container(
height: MediaQuery.of(context).size.height /
1.5,
// padding: EdgeInsets.only(
// top: 32,
// ),
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Container(
width: size.width * 0.94,
child: Column(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.only(
left: 10,
right: 10,
bottom: 20,
top: 20),
child: Column(
mainAxisAlignment:
MainAxisAlignment
.start,
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(
'Votre nom :',
style: TextStyle(
color: Color(
0xFF4053FCF),
fontSize: 16,
fontWeight:
FontWeight
.w600),
),
IconButton(
icon: Icon(
CommunityMaterialIcons
.pencil,
color: Colors
.grey,
),
onPressed: () {
myFocusNode
.requestFocus();
setState(() {
enableup =
true;
});
})
],
),
TextFormField(
controller:
_nameController,
enabled: enableup,
focusNode:
myFocusNode,
enableInteractiveSelection:
false,
keyboardType:
TextInputType
.text,
decoration: InputDecoration(
hintText: "$name",
hintStyle: TextStyle(
color: Colors
.grey,
fontSize:
14.0)),
),
I think you need to reload the Sharedpreference value.
SharedPreferences localStorage = await SharedPreferences.getInstance();
await localStorage.reload();
You should add the onChanged method to your TextFormField widgets to update the current state whenever a value is changed within.
// Example of adding onChanged to a TextFormField widget
TextFormField(
...,
onChanged: (String? value) {
// Update the state using setState.
setState(() => name = value);
},
),
App was working perfectly before and then I had to make some changes to allow or restrict calling feature in the app based on subscription level of the user, by passing the variable value from one screen to another using provider.
one Screen 1 i am using :
Future<void> _verifyPuchase(String id) async {
PurchaseDetails purchase = _hasPurchased(id);
if (purchase != null && purchase.status == PurchaseStatus.purchased) {
print(purchase.productID);
if (Platform.isIOS) {
await _iap.completePurchase(purchase);
print('Achats antérieurs........$purchase');
isPuchased = true;
}
isPuchased = true;
checkIsPurchsed(isPurchsed: isPuchased);
} else {
isPuchased = false;
checkIsPurchsed(isPurchsed: isPuchased);
}
}
and have i have a class on screen 1:
class checkIsPurchsed with ChangeNotifier{
bool isPurchsed;
checkIsPurchsed({this.isPurchsed});
notifyListeners();
}
and on screen 5 I have:
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.call), onPressed: () => onJoin("AudioCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.video_call), onPressed: () => onJoin("VideoCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
on screen 5 when i try to open a chat this is what i am getting :
the icon buttons on which i am checking the purchase status appear when chat is opened , but after adding the above mentioned modifications the chat itself isnt opening and instead i am getting the red screen.
Update 1:
class ChatPage extends StatefulWidget {
final User sender;
final String chatId;
final User second;
ChatPage({this.sender, this.chatId, this.second});
#override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
bool isBlocked = false;
final db = Firestore.instance;
CollectionReference chatReference;
final TextEditingController _textController = new TextEditingController();
bool _isWritting = false;
final _scaffoldKey = GlobalKey<ScaffoldState>();
//Ads _ads = new Ads();
#override
void initState() {
//_ads.myInterstitial()
//..load()
//..show();
print("object -${widget.chatId}");
super.initState();
chatReference =
db.collection("chats").document(widget.chatId).collection('messages');
checkblock();
}
var blockedBy;
checkblock() {
chatReference.document('blocked').snapshots().listen((onData) {
if (onData.data != null) {
blockedBy = onData.data['blockedBy'];
if (onData.data['isBlocked']) {
isBlocked = true;
} else {
isBlocked = false;
}
if (mounted) setState(() {});
}
// print(onData.data['blockedBy']);
});
}
List<Widget> generateSenderLayout(DocumentSnapshot documentSnapshot) {
return <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
child: documentSnapshot.data['image_url'] != ''
? InkWell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
margin: EdgeInsets.only(
top: 2.0, bottom: 2.0, right: 15),
child: Stack(
children: <Widget>[
CachedNetworkImage(
placeholder: (context, url) => Center(
child: CupertinoActivityIndicator(
radius: 10,
),
),
errorWidget: (context, url, error) =>
Icon(Icons.error),
height:
MediaQuery.of(context).size.height * .65,
width: MediaQuery.of(context).size.width * .9,
imageUrl:
documentSnapshot.data['image_url'] ?? '',
fit: BoxFit.fitWidth,
),
Container(
alignment: Alignment.bottomRight,
child:
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
),
)
],
),
height: 150,
width: 150.0,
color: secondryColor.withOpacity(.5),
padding: EdgeInsets.all(5),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: Text(
documentSnapshot.data["time"] != null
? DateFormat.yMMMd()
.add_jm()
.format(documentSnapshot.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
)),
)
],
),
onTap: () {
Navigator.of(context).push(
CupertinoPageRoute(
builder: (context) => LargeImage(
documentSnapshot.data['image_url'],
),
),
);
},
)
: Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(
top: 8.0, bottom: 8.0, left: 80.0, right: 10),
decoration: BoxDecoration(
color: primaryColor.withOpacity(.1),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd()
.add_jm()
.format(documentSnapshot
.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(
width: 5,
),
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
)
],
),
],
),
],
)),
),
],
),
),
];
}
_messagesIsRead(documentSnapshot) {
return <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: CircleAvatar(
backgroundColor: secondryColor,
radius: 25.0,
child: ClipRRect(
borderRadius: BorderRadius.circular(90),
child: CachedNetworkImage(
imageUrl: widget.second.imageUrl[0] ?? '',
useOldImageOnUrlChange: true,
placeholder: (context, url) => CupertinoActivityIndicator(
radius: 15,
),
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
),
onTap: () => showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return Info(widget.second, widget.sender, null);
}),
),
],
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: documentSnapshot.data['image_url'] != ''
? InkWell(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
new Container(
margin: EdgeInsets.only(
top: 2.0, bottom: 2.0, right: 15),
child: CachedNetworkImage(
placeholder: (context, url) => Center(
child: CupertinoActivityIndicator(
radius: 10,
),
),
errorWidget: (context, url, error) =>
Icon(Icons.error),
height: MediaQuery.of(context).size.height * .65,
width: MediaQuery.of(context).size.width * .9,
imageUrl:
documentSnapshot.data['image_url'] ?? '',
fit: BoxFit.fitWidth,
),
height: 150,
width: 150.0,
color: Color.fromRGBO(0, 0, 0, 0.2),
padding: EdgeInsets.all(5),
),
Padding(
padding: const EdgeInsets.only(right: 10),
child: Text(
documentSnapshot.data["time"] != null
? DateFormat.yMMMd()
.add_jm()
.format(documentSnapshot.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
)),
)
],
),
onTap: () {
Navigator.of(context).push(CupertinoPageRoute(
builder: (context) => LargeImage(
documentSnapshot.data['image_url'],
),
));
},
)
: Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(top: 8.0, bottom: 8.0, right: 10),
decoration: BoxDecoration(
color: secondryColor.withOpacity(.3),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd()
.add_jm()
.format(documentSnapshot
.data["time"]
.toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
],
)),
),
],
),
),
];
}
List<Widget> generateReceiverLayout(DocumentSnapshot documentSnapshot) {
if (!documentSnapshot.data['isRead']) {
chatReference.document(documentSnapshot.documentID).updateData({
'isRead': true,
});
return _messagesIsRead(documentSnapshot);
}
return _messagesIsRead(documentSnapshot);
}
generateMessages(AsyncSnapshot<QuerySnapshot> snapshot) {
return snapshot.data.documents
.map<Widget>((doc) => Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: doc.data['type'] == "Call"
? [
Text(doc.data["time"] != null
? "${doc.data['text']} : " +
DateFormat.yMMMd()
.add_jm()
.format(doc.data["time"].toDate())
.toString() +
" by ${doc.data['sender_id'] == widget.sender.id ? "You" : "${widget.second.name}"}"
: "")
]
: doc.data['sender_id'] != widget.sender.id
? generateReceiverLayout(
doc,
)
: generateSenderLayout(doc)),
))
.toList();
}
#override
Widget build(BuildContext context) {
ChangeNotifierProvider<checkIsPurchsed>(
create: (context)=>checkblock(),
child: Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
elevation: 0,
title: Text(widget.second.name),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
onPressed: () => Navigator.pop(context),
),
actions: <Widget>[
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.call), onPressed: () => onJoin("AudioCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
Consumer<checkIsPurchsed>(
builder: (context,isPurchsed,child){
return isPurchsed.isPurchsed ? IconButton(
icon: Icon(Icons.video_call), onPressed: () => onJoin("VideoCall"),
):Center(child: Text(
'Sorry you have not subscribe the package',
),);
},
),
}
The problematic area is:
#override
Widget build(BuildContext context) {
ChangeNotifierProvider<checkIsPurchsed>(
There's no return in front of ChangeNotifierProvider, so it doesn't return a Widget. Correct would be:
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<checkIsPurchsed>(