List View in Flutter - flutter

I've been having trouble understanding how to correctly list of all the items from the API call, I have now made it to display the first from the list of the API, by adding the index [0] after the API response. I realize I can do that with the itemCount which I currently had it set at 1 since I can't seem to comprehend how to use the .length option that lists all of the item from the API. Here is the code:
API CALL:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:practice/models/jobs.dart';
Future<JobData> fetchJobs() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return JobData.fromJson(jsonDecode(response.body)[0]);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load jobs');
}
}
THE JOB MODEL:
import 'package:flutter/foundation.dart';
class JobData extends ChangeNotifier {
final int userId;
final int id;
final String title;
final String body;
JobData({this.userId, this.id, this.title, this.body});
factory JobData.fromJson(Map<String, dynamic> json) {
return JobData(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
AND THE WIDGET WHERE IT IS RENDERED:
import 'package:flutter/material.dart';
import 'package:practice/models/jobs.dart';
import 'package:provider/provider.dart';
import 'package:practice/services/api_calls.dart';
class JobList extends StatefulWidget {
#override
_JobListState createState() => _JobListState();
}
class _JobListState extends State<JobList> {
Future<JobData> futureJobs;
#override
void initState() {
super.initState();
futureJobs = fetchJobs();
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Today',
),
SizedBox(
height: 12.0,
),
Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12.0),
topRight: Radius.circular(12.0),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Text(
'Jobs #1',
style: TextStyle(fontWeight: FontWeight.w700),
),
),
FlatButton(
textColor: Colors.blue[700],
minWidth: 0,
child: Text('View'),
onPressed: () {
},
),
],
),
),
Container(
padding: EdgeInsets.all(18.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(12.0),
bottomRight: Radius.circular(12.0),
),
),
child: Consumer<JobData>(
builder: (context, jobData, child) {
return SizedBox(
height: 200,
child: FutureBuilder<JobData>(
future: futureJobs,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('${snapshot.data.userId}',
style: TextStyle(color: Colors.black),
Text(
'${snapshot.data.id}',
style: TextStyle(color: Colors.black),
),
Text(
'${snapshot.data.title}',
style: TextStyle(color: Colors.black),
),
Text(
'${snapshot.data.body}',
style: TextStyle(color: Colors.black),
),
],
);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
);
},
),
),
SizedBox(
height: 16.0,
),
],
);
}
}
Do you I need to make a list out of the returned API response first and then display it's length?
Thanks in advance!

Try changing this..
Future<List<JobData>> fetchJobs() async {
List<JobData> jobs = new List();
final response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var jobsJson = jsonDecode(response.body);
for(int i = 0; i < jobsJson.length; i++) {
jobs.add(JobData.fromJson(jobsJson[i]));
}
return jobs;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load jobs');
}
}
then..
Future<List<JobData>> futureJobs;
inside ListView
itemCount: futureJobs.length,
then take each job from futureJobs list using index and show in ListView()

Related

Provider not rebuilding on flutter

suddently from nowhere i came up with provider not re rendering my home page when it's updated. I've inspected it and it IS UPDATED. It has newer data when i change it in firebase but the UI won't re-render showing the new data. That's my code:
Main function
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:my_event_app/pages/HomePage/home_page.dart';
import 'package:my_event_app/pages/Login/login_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// is not restarted.
primarySwatch: Colors.blue,
),
home: const Wrapper(),
),
);
}
}
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.userChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, userSnapshot) {
if (userSnapshot.hasData) {
Provider.of<UserModel>(context, listen: true)
.fromJson(userSnapshot.data!.data());
return const HomePage();
}
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
});
} else {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
And this is the home page:
import 'package:flutter/material.dart';
import 'package:my_event_app/http/auth/user/sign_out.dart';
import 'package:my_event_app/pages/Create_Event/create_event_page.dart';
import 'package:my_event_app/pages/Onboarding/onboarding_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:my_event_app/widgets/event_card_widget.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Consumer<UserModel>(builder: (context, user, child) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black, size: 30),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const OnboardingPage();
}));
},
),
actions: [
IconButton(
icon: const Icon(
Icons.arrow_forward_ios_sharp,
color: Colors.black,
),
onPressed: () {
signOut();
},
),
],
elevation: 0,
backgroundColor: Colors.white,
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
"https://cdnnmundo1.img.sputniknews.com/img/07e5/09/13/1116212032_100:0:1273:1173_1920x0_80_0_0_efb734331af13dfe11ff6d43293c60e2.png"),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.orange[400],
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Center(
child: IconButton(
color: Colors.white,
onPressed: () {
// Navigate to add event widget
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return const CreateEventPage();
}));
},
icon: const Icon(Icons.add),
),
),
),
],
),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: Text('Welcome, ${user.name}',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
),
const SizedBox(height: 32),
Container(
padding: const EdgeInsets.all(16),
height: 100,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(alignment: Alignment.center, children: [
SizedBox(
height: 45,
width: 45,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.orange[400]),
value: 14 / 20,
semanticsValue: "14/20",
color: Colors.black,
),
),
const Text("78%",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
]),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Weekly progress",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Text("14/20 tasks completed"),
],
),
const Icon(Icons.bar_chart),
],
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Text("You have 5 tasks for today",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Icon(Icons.calendar_today_outlined)
],
),
),
_renderEvents(user),
],
),
),
),
);
});
}
}
Column _renderEvents(UserModel user) {
return Column(
children: [
for (var event in user.events)
EventCard(
eventId: event,
),
],
);
}
And here's the provider:
import 'package:flutter/material.dart';
class UserModel extends ChangeNotifier {
String _name = '';
String _surnames = '';
String _uid = '';
String _email = '';
List<dynamic> _events = [];
String get name => _name;
String get surnames => _surnames;
String get uid => _uid;
String get email => _email;
List<dynamic> get events => _events;
UserModel();
set name(String value) {
_name = value;
notifyListeners();
}
set surnames(String value) {
_surnames = value;
notifyListeners();
}
set uid(String value) {
_uid = value;
notifyListeners();
}
set email(String value) {
_email = value;
notifyListeners();
}
set events(List<dynamic> value) {
_events = value;
notifyListeners();
}
void addEvent(String event) {
_events.add(event);
notifyListeners();
}
void removeEvent(String event) {
_events.remove(event);
notifyListeners();
}
void updateUser(String name, String uid) {
name = name;
uid = uid;
notifyListeners();
}
void clearUser() {
_name = '';
_uid = '';
notifyListeners();
}
Map<String, dynamic> toJson() {
return {
'name': _name,
'surnames': _surnames,
'uid': _uid,
'email': _email,
'events': _events
};
}
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
} catch (e) {
print(e);
}
}
}
```
As you can see i use Consumer in order to read data and i have a change notifier in the begginig, but it won't re render and show for example new name if i change it in fireabase.
Thank you so much!
You are using fromJson method to update values in UserModel, but it does not call notifyListeners. Add notifyListeners(); to the end of this method:
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
notifyListeners(); // add this
} catch (e) {
print(e);
}
}
Also some other things:
Consider declaring class UserModel with ChangeNotifier instead of class UserModel extends ChangeNotifier.
fromJson methods usually are acting as factory methods, meaning these return a new instance, and don't set members in an existing instance.
Instead of Provider.of<UserModel>(context, listen: true).fromJson(userSnapshot.data!.data()); you can try: context.read<UserModel>().fromJson(userSnapshot.data!.data());. Here you don't really need listening, you just want to find the provider and call fromJson. Your Consumer is the one which is listening to the changes accordingly.

click on raised button failed without any error

I have a very Strange error in my app. When i try to click on the button "PARIER" it does absolutely Nothing and i Don't see why. I called Prono_Match() but it doesn't work at all, i have no syntax errors and it does Nothing. I have tried called an other page but it is same. Why it ignores totally the call of Prono_Match ??? It is very very Strange :(
My code :
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'menu_member.dart';
import 'globals.dart' as globals;
import 'appbar_draw.dart';
import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:intl/intl.dart';
import 'prono_match.dart';
// Create a Form widget.
class Affiche_Matchs extends StatefulWidget {
#override
_Affiche_Matchs_State createState() {
return _Affiche_Matchs_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_Matchs_State extends State<Affiche_Matchs> {
#override
bool load=false;
bool visible=false;
String idmatch="";
String prono="";
List<String> radioValues = [];
Future<List<Match>> grid;
Future <List<Match>> Liste_Match_Display() async {
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/display_matchs.php';
var data = {
'id_membre': globals.id_membre,
};
var data_encode = jsonEncode(data);
// Starting Web API Call.
var response = await http.post(url,body: data_encode,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
var i=0;
for (var u in jsondata) {
i=i+1;
Match match = Match(u["id"],u["equipe1"],u["equipe2"],u["dated"],u["heured"]);
Matchs.add(match);
radioValues.add("");
}
return Matchs;
}
void initState() {
super.initState();
grid = Liste_Match_Display();
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Colors.blue[300],Colors.blue[400]
],
),
),
),
Scaffold(
appBar: drawappbar(true),
backgroundColor: Colors.transparent,
drawer: new DrawerOnly(className: Affiche_Matchs()),
body:
Center(
child : Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height*0.8,
width: MediaQuery.of(context).size.width,
child:
FutureBuilder(
future: grid,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Center(child: new CircularProgressIndicator(),);
default:
if(snapshot.hasError) {
return new Center(child: new Text('Error: ${snapshot.error}'),);
}
else {
List<Match> values = snapshot.data;
if (values.isEmpty) {
return Container(
child: Center(
child: Text("Aucun match disponible !!!",style: TextStyle(color: Colors.white))
)
);
}
else {
Match lastitem;
lastitem=values[0];
int i=0;
return ListView.builder(itemCount: values.length,itemBuilder: (_,index) {
bool header = lastitem.date_debut !=
values[index].date_debut;
lastitem = values[index];
var parsedDate = DateTime.parse(values[index].date_debut);
final formatter = new DateFormat('dd/MM/yyyy');
var dat = formatter.format(parsedDate);
return Column(
children: [
(header || index == 0)
?
Container(
height: 30,
margin: EdgeInsets.only(top:10),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue[700],
width: 2,
),
color: Colors.blue[700]
),
child : new Text(dat,textAlign: TextAlign.center,style: TextStyle(fontSize: 18.0,fontWeight: FontWeight.w500,color: Colors.white),),
)// here// display header
:
Container(),
Container(
margin: EdgeInsets.only(top:20,bottom:20),
child: Center(
child: Text(values[index].heure_debut,style: TextStyle(color: Colors.white)),
),
),
Container(
margin: const EdgeInsets.only(bottom:10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Text(values[index].equipe1+" - "+values[index].equipe2,style: TextStyle(fontSize:10,color: Colors.white)),
),
]
),
),
Container(
margin: const EdgeInsets.only(left:5),
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
child: Text('PARIER'),
onPressed: () {
idmatch=values[index].id;
print(idmatch);
setState(() {
visible=true;
});
Prono_Match(idmatch: idmatch);
},
),
),
]
);
}
);
}
};
};
}
),
),
]
)
)
)
]
);
}
}
class Match {
final String id;
final String equipe1;
final String equipe2;
final String date_debut;
final String heure_debut;
const Match(this.id,this.equipe1, this.equipe2,this.date_debut,this.heure_debut);
}
I'm guessing you're trying to show Prono_Match page when pressing on the button, although that's not how you call a page in flutter, instead do this :
onPressed: () {
idmatch=values[index].id;
print(idmatch);
setState(() {
visible=true;
});
Navigator.push(context,MaterialPageRoute(builder: (context)=>Prono_Match(idmatch: idmatch),),
);
},

How display a list of data order by date?

Here is the result now :
It is much better but i Don't know why BELSHINA BOBRUISK is not centered vertically. As you can see the others team are centered vertically. FC SMOELVITCHI is centered. Strange. Too i would like display the "2020-06-01" in mobile format so if it is french mobile it will be "01-06-2020".
And another question i Don't know why my mobile app use the theme with fontfamily but not all. I have some pages with a part of data in good family font and the other part in default font, its is really strange
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'package:flutter_app/menu_member.dart';
import 'package:flutter_app/globals.dart' as globals;
import 'package:flutter_app/appbar_draw.dart';
// Create a Form widget.
class Affiche_Matchs extends StatefulWidget {
#override
_Affiche_Matchs_State createState() {
return _Affiche_Matchs_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_Matchs_State extends State<Affiche_Matchs> {
#override
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> grid;
Future <List<Match>> Liste_Match_Display() async {
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/display_matchs.php';
// Starting Web API Call.
var response = await http.get(url,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
var i=0;
for (var u in jsondata) {
i=i+1;
Match match = Match(u["id"],u["equipe1"],u["equipe2"],u["type_prono"],u["date_debut"],u["heure_debut"]);
Matchs.add(match);
radioValues.add("");
}
return Matchs;
}
void initState() {
grid = Liste_Match_Display();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: drawappbar(true),
drawer: new DrawerOnly(className: Affiche_Matchs()),
body:
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child:
FutureBuilder(
future: grid,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Center(child: new CircularProgressIndicator(),);
default:
if(snapshot.hasError) {
return new Center(child: new Text('Error: ${snapshot.error}'),);
}
else {
List<Match> values = snapshot.data;
Match lastitem;
lastitem=values[0];
if (values.isEmpty) {
return Container(
child: Center(
child: Text("Aucun match disponible !!!")
)
);
}
else {
return Form(
key: _formKey,
child: ListView.builder(itemCount: values.length,itemBuilder: (_,index) {
bool header = lastitem.date_debut !=
values[index].date_debut;
lastitem = values[index];
return Column(
children: [
(header || index == 0)
?
Container(
height: 30,
margin: EdgeInsets.only(top:10),
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue[700],
width: 2,
),
color: Colors.blue[700]
),
child : new Text(values[index].date_debut,textAlign: TextAlign.center,style: TextStyle(fontSize: 18.0,fontWeight: FontWeight.w500,color: Colors.white),),
)// here display header
:
Container(),
Container(
margin: EdgeInsets.only(top:20,bottom:20),
child: Center(
child: Text(values[index].heure_debut),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(values[index].equipe1,style: TextStyle(fontSize:12)),
draw_grid("1", index, values[index].typeprono),
draw_grid("N", index, values[index].typeprono), //
draw_grid("2", index, values[index].typeprono), //
Text(values[index].equipe2,style: TextStyle(fontSize:12)),
]
),
]
);
}
)
);
}
};
};
}
),
),
);
}
draw_grid (String choix, int index,String type_prono) {
if (type_prono.contains(choix)) {
return new InkWell(
onTap: () {
setState(() {
if (radioValues[index] == choix) {
radioValues[index] = "";
}
else {
radioValues[index] = choix;
}
});
print(radioValues);
},
child:
Container(
height: 30.0,
width: 30.0,
margin: EdgeInsets.only(right: 2,left: 2),
child: new Center(
child: new Text(choix,
style: new TextStyle(
color:
radioValues[index] == choix ? Colors.white : Colors.red,
//fontWeight: FontWeight.bold,
fontSize: 18.0, fontWeight: FontWeight.w900)),
),
decoration: new BoxDecoration(
color: radioValues[index] == choix
? Colors.red
: Colors.white,
border: new Border.all(
width: 2.0,
color: radioValues[index] == choix
? Colors.red
: Colors.red),
borderRadius: const BorderRadius.all(const Radius.circular(5)),
),
),
);
}
else {
return Text("");
}
}
}
class Match {
final String id;
final String equipe1;
final String equipe2;
final String typeprono;
final String date_debut;
final String heure_debut;
const Match(this.id,this.equipe1, this.equipe2, this.typeprono,this.date_debut,this.heure_debut);
}
You have to sort you list base on date and then you can display by checking you have to display header or not.
Following code help you to understand more and then you can implement.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class DeleteWidget extends StatefulWidget {
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> grid;
Future<List<Match>> Liste_Match_Display() async {
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/display_matchs.php';
// Starting Web API Call.
var response = await http.get(url, headers: {
'content-type': 'application/json',
'accept': 'application/json',
'authorization':
'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3MiOjE1ODg5NTQ0NDgsImlhdCI6MTU4ODk1NDQ0OCwiZXhwIjoxNTg5MDE0NDQ4LCJkYXRhIjp7ImlkIjoiMSIsImVtYWlsIjoicGFzMzBAbmV0Y291cnJpZXIuY29tIn19.-jcyoxtkNVWWagune6EOjInjBgObyxf9gweXJrA2MxLL5fRTW1pkFSFrJOW8uYzhVpaZ4CF9A-c_m8akUq74NA '
});
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
var i = 0;
for (var u in jsondata) {
i = i + 1;
Match match = Match(u["id"], u["equipe1"], u["equipe2"], u["type_prono"],
u["date_debut"], u["heure_debut"]);
Matchs.add(match);
radioValues.add("");
}
return Matchs;
}
void initState() {
grid = Liste_Match_Display();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: drawappbar(true),
//drawer: new DrawerOnly(className: Affiche_Matchs()),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: grid,
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Center(
child: new CircularProgressIndicator(),
);
default:
if (snapshot.hasError) {
return new Center(
child: new Text('Error: ${snapshot.error}'),
);
} else {
List<Match> values = snapshot.data;
Match lastitem;
lastitem = values[0];
if (values.isEmpty) {
return Container(
child: Center(
child: Text("Aucun match disponible !!!")));
} else {
return Form(
key: _formKey,
child: ListView.builder(
itemCount: values.length,
itemBuilder: (_, index) {
bool header = lastitem.date_debut !=
values[index].date_debut;
lastitem = values[index];
return Column(
children: [
(header || index == 0)
? Container(
height: 30,
margin: EdgeInsets.only(top: 10),
width: MediaQuery.of(context)
.size
.width,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue[700],
width: 2,
),
color: Colors.blue[700]),
child: new Text(
values[index].date_debut,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Colors.white),
),
) // here display header
: Container(),
Text(values[index].heure_debut),
Text(values[index].equipe1),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
draw_grid("1", index,
values[index].typeprono),
draw_grid("N", index,
values[index].typeprono), //
draw_grid("2", index,
values[index].typeprono), //
],
),
Text(values[index].equipe2),
// here display whole item
],
);
}));
}
}
;
}
;
}),
),
);
}
draw_grid(String choix, int index, String type_prono) {
if (type_prono.contains(choix)) {
return new InkWell(
onTap: () {
setState(() {
if (radioValues[index] == choix) {
radioValues[index] = "";
} else {
radioValues[index] = choix;
}
});
print(radioValues);
},
child: Container(
height: 40.0,
width: 40.0,
child: new Center(
child: new Text(choix,
style: new TextStyle(
color:
radioValues[index] == choix ? Colors.white : Colors.red,
//fontWeight: FontWeight.bold,
fontSize: 18.0,
fontWeight: FontWeight.w900)),
),
decoration: new BoxDecoration(
color: radioValues[index] == choix ? Colors.red : Colors.white,
border: new Border.all(
width: 2.0,
color: radioValues[index] == choix ? Colors.red : Colors.red),
borderRadius: const BorderRadius.all(const Radius.circular(5)),
),
),
);
} else {
return Text("");
}
}
}
class Match {
final String id;
final String equipe1;
final String equipe2;
final String typeprono;
final String date_debut;
final String heure_debut;
const Match(this.id, this.equipe1, this.equipe2, this.typeprono,
this.date_debut, this.heure_debut);
}

How to pass data back from a widget?

I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.

is there anybody who can help me parsing json data to another page using Hero Widget?

I want somebody to help me to parse JSON data to another Page in HERO widget in the flutter. I parse data to the first page but failed to parse data to another page
make the model for JSON data use PODO style
like this which will deal with all JSON data to parsed to the view class.
class ProductResponse{
List<ProductDetail> results;
ProductResponse({this.results});
ProductResponse.fromJson(Map<String,dynamic> json){
if(json['results'] !=null){
results=new List<ProductDetail>();
json['results'].forEach((v){
results.add(new ProductDetail.fromJson(v));
});
}
}
Map<String,dynamic> toJson(){
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.results != null) {
data['results'] = this.results.map((v) => v.toJson()).toList();
}
return data;
}
}
class ProductDetail{
int phone;
int price;
int qty;
String amount;
String place;
String image_name;
String image_url;
String vendor_name;
String description;
String category;
String productName;
String images;
ProductDetail({this.phone, this.price, this.qty, this.amount, this.vendor_name,
this.description, this.category, this.productName, this.images,this.image_name,this.image_url,this.place});
ProductDetail.fromJson(Map<String,dynamic> json){
phone = json['phone'];
price = json["price"];
qty = json['qty'];
amount =json['amount'];
vendor_name =json['vendor_name'];
description = json['description'];
category = json['category'];
images = json['images'];
productName = json['productName'];
image_url =json['image_url'];
image_name =json['image_name'];
place =json['place'];
}
Map<String,dynamic> toJson(){
final Map<String,dynamic> data =new Map<String,dynamic>();
data['phone'] =this.phone;
data['price'] =this.price;
data['qty'] =this.qty;
data['amount'] =this.amount;
data['vendor_name'] =this.vendor_name;
data['description'] =this.description;
data['category'] =this.category;
data['productName'] =this.productName;
data['images'] =this.images;
data['place'] = this.place;
data['image_url'] =this.image_url;
data['image_name'] =this.image_name;
return data;
}
}
make the class which will make the request to the server
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:kilimo_biashara1/Model/Dialogs.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/Products/ProductPage.dart';
class Products extends StatefulWidget {
#override
_ProductsState createState() => _ProductsState();
}
class _ProductsState extends State<Products> {
String url = "put your api url here";
ProductResponse detail;// declare the class from PODO
fetchProduct() async {
var response = await http.get(url);
var decodeJson = jsonDecode(response.body);
print("response" + response.body);
setState(() {
detail = ProductResponse.fromJson(decodeJson);
});
print(detail);//debug
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: detail == null
? Center(
child: CircularProgressIndicator(),
)
: StaggeredGridView.countBuilder(
crossAxisCount: 4,
itemCount: detail.results.length,
itemBuilder: (BuildContext context, int index) {
return ProductPage(
detail: detail.results[index]
); //Return the product page
},
staggeredTileBuilder: (_) => StaggeredTile.fit(2),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
)
);
}
}
here are sample codes of the product page.in this product page it will show cards with few details in it
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Products/DetailProduct.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
class ProductPage extends StatelessWidget {
final ProductDetail detail;
ProductPage({#required this.detail});
#override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.0),
),
elevation: 4.0,
margin: EdgeInsets.all(4.0),
child: InkWell(
radius: 4.0,
child: getCardView(context),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
DetailProduct(
detail: detail,
),
),
);
},
),
);
}
//////
getCardView(BuildContext context){
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,//this key /tag will be the same with another page also
child: Container(
height: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
image: DecorationImage(
image: NetworkImage("${detail.image_url}"
,
),
fit: BoxFit.cover),
),
), ),
// Image.asset("images/ndz.jpg"),
Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("sold by: "+
detail.vendor_name,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("product: "+
detail.productName,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
Text("price: ${detail.price} ${detail.amount}"
,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: Theme
.of(context)
.textTheme
.body2,
),
],
),
),
],
);
}
}
when it is clicked/tapped it will direct to the other page which will provide full details about the products
import 'package:flutter/material.dart';
import 'package:kilimo_biashara1/Model/ProductDetail.dart';
import 'package:kilimo_biashara1/payment/Payment.dart';
class DetailProduct extends StatelessWidget {
final ProductDetail detail;
DetailProduct({this.detail});
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
persistentFooterButtons: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(15.0, 5.0, 40.0, 5.0),
child: new Text("SHOPPING",
style: new TextStyle(fontSize: 25.0,color: Colors.green,fontWeight: FontWeight.bold),
),
),
new FlatButton(
child: new Icon(Icons.shopping_cart,size: 35.0,),
onPressed:(){
Navigator.of(context).push(
new MaterialPageRoute(
builder: (BuildContext context)=>new Payment()));
} ,
),
],
body:new Scaffold(
body:ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Hero(
tag: detail,
child: Container(
height: 250.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
shape: BoxShape.rectangle,
image: DecorationImage(
image: NetworkImage(
"${detail.image_url}",
),
fit: BoxFit.cover,
),
),
),
),
// Image.asset("images/ndz.jpg"),
SizedBox(
height: 16.0,
),
Text("vendor name: "+
detail.vendor_name,
style: Theme.of(context).textTheme.title,
),
SizedBox(
height: 16.0,
),
Text("product name: "+
detail.productName,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("vendor place: "+
detail.place,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("price: ${detail.price} ${detail.amount}",
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("short description: "+
detail.description,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("Category: "+detail.category,
style: Theme.of(context).textTheme.subhead,
),
SizedBox(
height: 16.0,
),
Text("contacts: ${detail.phone}",
style: Theme.of(context).textTheme.subhead,
),
],
),
),
],
),
),
),
);
}
}
after following these steps you should reach to a point where you parse data from the API with hero widget