How change the visual of List of Radio button? - flutter

I have a bet game it is a list of game and user need to choose between 3 choices (only one choice) (1,N,2) so i use radio in flutter for do it.
1 - Here is the result i have now :
2 - Here is the result i want to have :
I need have the same radio 1 N 2 without using any images, i think it is possible to do it using clipoval perhaps with rounded borders. Here the selected radio is in RED in background so when i click on another radio it needs change the color of radio (red background and white color font if selected and white background and red color font if not selected)
And now here is my entire code for the grid :
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/grille_lotosport.dart';
// Create
a Form widget.
class Affiche_grille extends StatefulWidget {
#override
String id;
Affiche_grille({Key key, #required this.id}) : super(key: key);
_Affiche_grille_State createState() {
return _Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> grid;
Future <List<Match>> Grille_display() async {
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': widget.id};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data),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 = [];
for (var u in jsondata) {
Match match = Match(u["equipe1"],u["equipe2"],u["type_prono"]);
Matchs.add(match);
radioValues.add("N");
}
return Matchs;
}
void initState() {
grid = Grille_display();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('GRILLE LOTOSPORT'),
),
drawer: new DrawerOnly(),
body:
Container(
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;
return Form(
key: _formKey,
child: ListView(
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Libelle Match"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("N"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("2"),
numeric: false,
tooltip: "",
),
],
rows:
List.generate(values.length, (index) {
return DataRow(
cells: [
DataCell(
Text(values[index].equipe1.toString() +
" - " +
values[index].equipe2.toString()),
),
DataCell(
Radio(
value: "1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value: "N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value: "2",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
]
);
}).toList(),
),
Center(
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
],
)
);
};
};
},
),
),
),
);
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'https://www.easytrafic.fr/game_app/valide_grid.php';
var concatenate='';
radioValues.forEach((item){
concatenate=concatenate+item;
});
var id_grille=widget.id;
// Store all data with Param Name.
var data = {'id_membre':globals.id_membre, 'id_grille':id_grille,'result':concatenate};
var grille_encode=jsonEncode(data);
// Starting Web API Call.
var response = await http.post(url, body: grille_encode,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
print(response.body);
// Getting Server response into variable.
Map <String,dynamic> map2 = json.decode(response.body);
// If the Response Message is Matched.
if(map2["status"] == 1)
{
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(map2["message"]),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Affiche_Liste_grille()),
);
},
),
],
);
},
);
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(map2["message"]),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
class Match {
final String equipe1;
final String equipe2;
final String typeprono;
const Match(this.equipe1, this.equipe2, this.typeprono);
}

I have found the solution, i Don't understand why it works but it works now with allmy corrections. I Don't understand why when i put a setstate all elements are updated correctly, i put no code in the setstate but all is now updated, it is very strange

Related

I'm trying to get the contacts from the emulator to a dropdownlist and let the user to select one. I'm getting an error

Probably the contacts have duplicates, but I want the duplicates to be accepted. The basic idea is to access the contact list and populate the values to a dropdownMenu and let the user to select a contact from there and save to a file. I have already initialised the dropdownMenu with a string "Select a contact" through a variable.
Exception has occurred.
_AssertionError ('package:flutter/src/material/dropdown.dart': Failed assertion: line 890 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1': There should be exactly one item with [DropdownButton]'s value: Select a contact.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value)
Here is the complete code
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:io';
import 'dart:convert';
import 'package:url_launcher/url_launcher.dart';
import 'package:path_provider/path_provider.dart';
class Interface extends StatelessWidget {
const Interface({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('pAM'),
),
body: const ContactSelector(),
);
}
}
class ContactSelector extends StatefulWidget {
const ContactSelector({super.key});
#override
_ContactSelectorState createState() => _ContactSelectorState();
}
class _ContactSelectorState extends State<ContactSelector> {
Contact _selectedContact = Contact();
late bool _isTrue;
late Iterable<Contact> _contacts;
List<DropdownMenuItem<String>> _dropdownItems = [];
String _selectedName = "Select Contact";
//late List<DropdownMenuItem<String>> _dropdownItems;
#override
void initState() {
super.initState();
_getContacts();
_selectedName = _dropdownItems.isNotEmpty
? _dropdownItems[0].value!
: 'Select a contact';
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
if (_dropdownItems != null)
DropdownButton<String>(
value: _selectedName,
items: _dropdownItems,
onChanged: (newValue) {
_onContactChanged(newValue!);
},
)
else
const Text("Loading...")
],
);
}
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
void _sendMessage(String message) async {
String phoneNumber = _selectedContact.phones.toString();
Uri uri = Uri(
scheme: 'sms',
path: phoneNumber,
query: encodeQueryParameters(<String, String>{
'body': 'Welcome to pAM',
}),
);
if (await canLaunchUrl(uri)) {
await canLaunchUrl(uri);
} else {
throw 'Could not send SMS';
}
}
_getContacts() async {
_contacts = await ContactsService.getContacts(withThumbnails: false);
_dropdownItems = _contacts
.map((c) => DropdownMenuItem(
value: c.displayName,
child: Text(c.displayName.toString()),
))
.toList();
setState(() {});
}
_onContactChanged(String newValue) {
setState(() {
_selectedName = newValue;
_selectedContact =
_contacts.firstWhere((c) => c.displayName == _selectedName);
});
_saveContactToFile(_selectedContact);
_readJson();
}
_saveContactToFile(Contact contact) async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/selected_contact.txt');
if (!(await file.exists())) {
file.create();
}
file.writeAsString(jsonEncode(contact.toMap()));
}
void _readJson() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/true.json');
if (await file.exists()) {
final content = jsonDecode(await file.readAsString());
if (content["isTrue"]) {
_promptMessage();
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Reminder'),
content: const Text(
"You can continue your work, remember your loved ones misses you"),
actions: <Widget>[
ElevatedButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
}
}
_promptMessage() {
if (_isTrue) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Select a message'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
InkWell(
child: const Text('How are you?'),
onTap: () {
_sendMessage('How are you?');
}),
InkWell(
child: const Text('What are you up to?'),
onTap: () {
_sendMessage('What are you up to?');
}),
InkWell(
child: const Text('What is for dinner?'),
onTap: () {
_sendMessage('What is for dinner?');
}),
],
),
),
actions: <Widget>[
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
This is the key part of the error message:
There should be exactly one item with [DropdownButton]'s value: Select a contact.
You are setting the value of the DropdownButton to "Select a contact" (presumably because _dropdownItems.isNotEmpty == false), but none of the DropdownMenuItems that you have given to the DropdownButton via its items property has "Select a contact" as its value. You might want to look into the use of the hint property to show the "Select a contact", well, hint.
Something like the (untested) code below:
DropdownButton<String>(
hint: Text("Select a contact")
value: _dropdownItems.isNotEmpty ? _dropdownItems.first.value : null,
items: _dropdownItems,
onChanged: (newValue) {
_onContactChanged(newValue!);
},
)

The search bar does not return any results

I am trying to add a search function in my flutter app, the search bar is showing and there's not errors but its not working and it doesn't return any results.
the data list is from an API that I already called using the rest API
// ignore_for_file: use_key_in_widget_constructors, avoid_print, avoid_unnecessary_containers, curly_braces_in_flow_control_structures, prefer_const_constructors, non_constant_identifier_names, unnecessary_new, avoid_function_literals_in_foreach_calls, unused_import, avoid_types_as_parameter_names, unused_label
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:myapp2/Service_Request/SR.dart';
import 'package:myapp2/main.dart';
import 'package:myapp2/Service_Request/second.dart';
import '../Classes/demandes.dart';
import 'SR_details.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DataFromAPI(),
);
}
}
class DataFromAPI extends StatefulWidget {
#override
_DataFromAPIState createState() => _DataFromAPIState();
}
List<Attributes> _MyAllData = [];
var _srAttributes = [];
class _DataFromAPIState extends State<DataFromAPI> {
#override
void initState() {
loadData().then((value) {
setState(() {
_srAttributes.addAll(value);
});
});
super.initState();
}
Future<List<Sr>> loadData() async {
try {
var response = await http.get(Uri.parse(
'http://192.168.1.30:9080/maxrest/rest/mbo/sr/?_lid=azizl&_lpwd=max12345m&_format=json'));
if (response.statusCode == 200) {
final jsonBody = json.decode(response.body);
Demandes data = Demandes.fromJson(jsonBody);
final srAttributes = data.srMboSet.sr;
return srAttributes;
}
} catch (e) {
throw Exception(e.toString());
}
throw Exception("");
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text('Liste des Demandes'),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => SR()))),
),
body: FutureBuilder<List<Sr>?>(
future: loadData(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
} else {
return new ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: ((_, index) {
return index == 0
? _searchbar()
: new ListTile(
title: new Card(
margin: new EdgeInsets.symmetric(
vertical: 2.0, horizontal: 8.0),
elevation: 10,
child: new ListTile(
title: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(padding: new EdgeInsets.all(2.0)),
new Text(
'Ticket ID : ${snapshot.data![index].attributes.ticketid.content}'),
new Text(
'status : ${snapshot.data![index].attributes.status.content}'),
new Text(
'description : ${snapshot.data![index].attributes.description?.content}'),
new Text(
'Reported by : ${snapshot.data![index].attributes.reportedby.content}'),
new Text(
'Reoprt date : ${snapshot.data![index].attributes.statusdate.content}'),
],
),
trailing: Icon(Icons.arrow_forward_ios_rounded),
),
),
onTap: () {
Navigator.of(context)
.push(
new MaterialPageRoute(
builder: (BuildContext context) =>
new SrDetailsScreen(
sr: snapshot.data![index]),
),
)
.then((data) {});
});
}),
);
}
},
),
),
);
}
_searchbar() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(hintText: "Search ..."),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
_srAttributes = _MyAllData.where((srAttributes) {
var idticket = srAttributes.description!.content.toLowerCase();
return idticket.contains(text);
}).toList();
});
},
),
);
}
}
FutureBuilder loads values of current future. You are assigning a function result to FutureBuilder so its value always changes dynamically.
Create variable to keep Future's value.
Future<List<Sr>>? dataToLoad;
Whenever you want to load data from server ( for example, on text changed ):
setState((){
dataToLoad = loadData();
});
And use it in FutureBuilder:
FutureBuilder<List<Sr>?>(
future: dataToLoad,

Using json file to give appbar logo for flutter. Facing Loading issue while navigation

I wanted to show appbar logo for all the pages from a json file. Now the issue is if I use Futurebuilder
then in every page load, appbar logo awaits before showing. I tried to use shared preference but having issues. Maybe I am doing it wrong. I am a newbie in flutter. If possible please give the answer in simple way so I can understand. Or anyone can help me by creating that part for appbar. That will be helpful.
Here is my json file for logo
import 'dart:convert';
import 'package:models/app_logo.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './home_screen.dart';
import './login_screen.dart';
import '../constants.dart';
import '../screens/courses_screen.dart';
import '../screens/my_courses_screen.dart';
import '../screens/my_wishlist_screen.dart';
import '../screens/account_screen.dart';
import '../widgets/filter_widget.dart';
import '../providers/auth.dart';
import 'package:http/http.dart' as http;
class TabsScreen extends StatefulWidget {
#override
_TabsScreenState createState() => _TabsScreenState();
}
class _TabsScreenState extends State<TabsScreen> {
List<Widget> _pages = [
HomeScreen(),
LoginScreen(),
LoginScreen(),
LoginScreen(),
];
var _isInit = true;
var _isLoading = false;
int _selectedPageIndex = 0;
bool _isSearching = false;
final searchController = TextEditingController();
Future<AppLogo> futureLogo;
Future<AppLogo> fetchMyLogo() async {
var url = BASE_URL + '/app_logo';
try {
final response = await http.get(url);
print(response.body);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.body);
return AppLogo.fromJson(jsonDecode(response.body));
}
// print(extractedData);
} catch (error) {
throw (error);
}
}
#override
void initState() {
super.initState();
this.fetchMyLogo();
// Provider.of<Auth>(context).tryAutoLogin().then((_) {});
}
#override
void didChangeDependencies() {
if (_isInit) {
setState(() {
_isLoading = true;
});
final _isAuth = Provider.of<Auth>(context, listen: false).isAuth;
if (_isAuth) {
_pages = [
HomeScreen(),
MyCoursesScreen(),
MyWishlistScreen(),
AccountScreen(),
];
}
}
_isInit = false;
super.didChangeDependencies();
}
void _handleSubmitted(String value) {
final searchText = searchController.text;
if (searchText.isEmpty) {
return;
}
searchController.clear();
Navigator.of(context).pushNamed(
CoursesScreen.routeName,
arguments: {
'category_id': null,
'seacrh_query': searchText,
'type': CoursesPageData.Search,
},
);
// print(searchText);
}
void _selectPage(int index) {
setState(() {
_selectedPageIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: kSecondaryColor, //change your color here
),
title: !_isSearching
? FutureBuilder<AppLogo>(
future: fetchMyLogo(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Container(),
);
} else {
if (snapshot.error != null) {
return Center(
child: Text("Error Occured"),
);
} else {
return Image.network(
snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
}
}
},
)
: TextFormField(
decoration: InputDecoration(
labelText: 'Search Here',
prefixIcon: Icon(
Icons.search,
color: Colors.grey,
),
),
controller: searchController,
onFieldSubmitted: _handleSubmitted,
),
backgroundColor: kBackgroundColor,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
color: kSecondaryColor,
),
onPressed: () {
setState(() {
_isSearching = !_isSearching;
});
}),
],
),
body: _pages[_selectedPageIndex],
floatingActionButton: FloatingActionButton(
onPressed: () => _showFilterModal(context),
child: Icon(Icons.filter_list),
backgroundColor: kDarkButtonBg,
),
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
items: [
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.school),
title: Text('Course'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.shopping_basket),
title: Text('My Course'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.favorite_border),
title: Text('Wishlist'),
),
BottomNavigationBarItem(
backgroundColor: kBackgroundColor,
icon: Icon(Icons.account_circle),
title: Text('Account'),
),
],
backgroundColor: kBackgroundColor,
unselectedItemColor: kSecondaryColor,
selectedItemColor: kSelectItemColor,
currentIndex: _selectedPageIndex,
type: BottomNavigationBarType.fixed,
),
);
}
}
There are some ways you can do to reduce the load time of the logo in your AppBar. Since this AppBar is common between the tabs, you should only load it once and avoid loading again every time the tab is changed.
First is to use StreamBuilder instead of FutureBuilder to reduce the number of loads.
// Create a StreamController
final _controller = StreamController<AppLogo>();
// Run fetchMyLogo() in your initState() like in your code
// In your fetchMyLogo(), add the result to the stream
fetchMyLogo() async {
// ... other lines
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
var logo = AppLogo.fromJson(jsonDecode(response.body));
_controller.add(logo);
}
// Then, listen to this logo in your StreamBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// ... other lines
title: !_isSearching
? StreamBuilder<AppLogo>(
stream: _controller.stream,
builder: (context, snapshot) {
// ... other lines
Second is to use the cached_network_image instead of Image.network, so that your logo image is cached, which reduce load time for network images.
return CachedNetworkImage(
imageUrl: snapshot.data.darkLogo,
fit: BoxFit.contain,
height: 27,
);
Small note: For each of the page in your _pages list, if you want the page to persist (not reload after every tab change), you can use the AutomaticKeepAliveClientMixin to persist the state of each page, or use IndexedStack like:
// The IndexedStack will load all pages initially
body: IndexedStack(
children: _pages,
index: _selectedPageIndex,
),
// ... other lines

Routig problem with storing data from fetch on other page

So here is my problem. I have to route to the page EditReservationScrenn from the general ReservationScreen I have, which simply shows a list of all reservations in a ListView.builder. On tab on one of those elements you then get routed(with an argument which is the complete item) to a next screen. On this EditReservationScreen the data of the passed argument gets presented on the screen as ListTiles. So here's the catch... By taping on the Icon which is presented as trailing you then get transferred to yet another screen where you can change your choice.
this all works. BUT. Whenever I tried to Navigator.pop(new argument) back to the EditReservationScreen I'm not able to update the screen with the new value nor to even store it because it is always overwritten.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:kinobuchungssystem/src/models/customer.dart';
import 'package:kinobuchungssystem/src/models/performance.dart';
import 'package:kinobuchungssystem/src/models/reservation.dart';
import 'package:kinobuchungssystem/src/models/seat.dart';
import 'package:kinobuchungssystem/src/provider/performance_provider.dart';
import 'package:kinobuchungssystem/src/provider/reservation_provider.dart';
import 'package:kinobuchungssystem/src/selections/add_performance_selection_screen.dart';
import 'package:kinobuchungssystem/src/selections/add_seat_selection_screen.dart';
import 'package:provider/provider.dart';
class EditReservationScreen extends StatefulWidget {
static const routeName = '/edit-reservation';
#override
_EditReservationScreenState createState() => _EditReservationScreenState();
}
class _EditReservationScreenState extends State<EditReservationScreen> {
List<Seat> selectedSeats = [];
List<Reservation> reservations = [];
Customer customer;
var _newPerformance = Performance(
id: null,
movie: null,
dateTime: null,
theater: null,
);
var _newReservation = Reservation(
id: null,
seat: null,
customer: null,
performance: null,
);
/* Future<void> _getPerformance() async {
final pId = await Navigator.of(context)
.pushNamed(AddPerformanceSelectionScreen.routeName);
Provider.of<PerformanceProvider>(context, listen: false)
.fetchAndSetPerformances();
var performance =
Provider.of<PerformanceProvider>(context, listen: false).findById(pId);
setState(() {
_newPerformance = Performance(
id: pId,
dateTime: performance.dateTime,
movie: performance.movie,
theater: performance.theater,
);
_newReservation = Reservation(
id: _newReservation.id,
seat: null,
customer: _newReservation.customer,
performance: performance,
);
});
} */
Future<void> _getReservation() async {
final reservation = /* final reservationId = */
ModalRoute.of(context).settings.arguments as Reservation;
Performance performance = reservation.performance;
_newReservation = Reservation(
customer: reservation.customer,
id: reservation.id,
performance: reservation.performance,
seat: reservation.seat,
);
customer = reservation.customer;
_newPerformance = Performance(
id: performance.id,
dateTime: performance.dateTime,
movie: performance.movie,
theater: performance.theater,
);
}
Future<void> _updateSeat(String id) async {
Map result = (await Navigator.of(context).pushNamed(
AddSeatSelectionScreen.routeName,
arguments: {'id': id, 'customer': _newReservation.customer}) as Map);
selectedSeats = List<Seat>.from((result['seats'] as List));
Performance p = result['performance'];
setState(() {
_newPerformance = Performance(
id: p.id,
dateTime: p.dateTime,
movie: p.movie,
theater: p.theater,
);
_newReservation = Reservation(
id: _newReservation.id,
seat: selectedSeats,
customer: _newReservation.customer,
performance: _newReservation.performance,
);
});
}
void _updateReservation() {
setState(() {
_newReservation = Reservation(
id: _newReservation.id,
seat: _newReservation.seat,
customer: _newReservation.customer,
performance: _newReservation.performance,
);
});
reservations.add(_newReservation);
_newPerformance = Performance(
id: _newPerformance.id,
dateTime: _newPerformance.dateTime,
movie: _newPerformance.movie,
theater: _newPerformance.theater,
);
Provider.of<ReservationProvider>(context, listen: false)
.addReservation(_newReservation);
Provider.of<PerformanceProvider>(context, listen: false)
.updatePerformance(_newPerformance.id, _newPerformance);
Navigator.pop(context);
}
String setSeatNumbers() {
List<int> seatNumbers = [];
List<Seat> seats = List<Seat>.from(_newReservation.seat);
String seatNumbersToString = '';
seats.forEach((seat) {
seatNumbers.add(seat.seatNumber);
});
seatNumbers.forEach((s) {
seatNumbersToString = '$seatNumbersToString, $s';
});
seatNumbersToString = seatNumbersToString.substring(1);
return seatNumbersToString;
}
#override
Widget build(BuildContext context) {
final reservation =
ModalRoute.of(context).settings.arguments as Reservation;
return Scaffold(
appBar: AppBar(
title: Text('Reservation bearbeiten'),
actions: <Widget>[
IconButton(icon: Icon(Icons.save), onPressed: _updateReservation),
],
),
body: FutureBuilder(
future: _getReservation(),
builder: (context, snapshot) => buildList(/* reservation */),
));
}
Widget buildList(/* Reservation reservation */) {
return Padding(
padding: EdgeInsets.all(10),
child: ListView(
children: <Widget>[
SizedBox(
height: 10,
),
ListTile(
leading: CircleAvatar(
backgroundColor: Colors.amber,
child: Icon(Icons.person),
),
title: Text(
'${_newReservation.customer.firstName} ${_newReservation.customer.lastName}'),
subtitle: Text('${_newReservation.customer.phoneNumber}'),
),
SizedBox(
height: 10,
),
Divider(),
SizedBox(
height: 10,
),
ListTile(
leading: CircleAvatar(
backgroundColor: Colors.amber,
child: Icon(Icons.movie),
),
title: Text(
'${_newReservation.performance.movie.title}',
),
subtitle: Text(
'${DateFormat.d().add_MMMM().add_y().format(_newReservation.performance.dateTime)} um ${DateFormat().add_Hm().format(_newReservation.performance.dateTime)}'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () async {
final pId = await Navigator.of(context)
.pushNamed(AddPerformanceSelectionScreen.routeName);
print(pId);
Provider.of<PerformanceProvider>(context, listen: false)
.fetchAndSetPerformances();
var performance =
Provider.of<PerformanceProvider>(context, listen: false)
.findById(pId);
setState(() {
_newPerformance = Performance(
id: pId,
dateTime: performance.dateTime,
movie: performance.movie,
theater: performance.theater,
);
_newReservation = Reservation(
id: _newReservation.id,
seat: null,
customer: _newReservation.customer,
performance: performance,
);
});
}),
),
SizedBox(
height: 10,
),
Divider(),
SizedBox(
height: 10,
),
ListTile(
leading: CircleAvatar(
backgroundColor: Colors.amber,
child: IconButton(
icon: Icon(
Icons.event_seat,
),
onPressed: () {},
),
),
title: _newReservation.seat == null
? Text('Sitze wählen')
: Text('Sitze: ${setSeatNumbers()}'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => _updateSeat(_newReservation.performance.id),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:kinobuchungssystem/src/provider/reservation_provider.dart';
import 'package:kinobuchungssystem/src/screens/reservation_screens/add_reservation_screen.dart';
import 'package:kinobuchungssystem/src/screens/reservation_screens/search_reservation_screen.dart';
import 'package:kinobuchungssystem/src/widgets/reservation_item.dart';
import 'package:provider/provider.dart';
class ReservationScreen extends StatelessWidget {
static const routeName = '/reservation';
Future<void> _refreshReservations(BuildContext context) async {
await Provider.of<ReservationProvider>(context, listen: false)
.fetchAndSetReservations();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Reservationen'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () =>
Navigator.of(context).pushNamed(AddReservationScreen.routeName),
),
],
),
floatingActionButton: FloatingActionButton.extended(
label: Text('Reservation suchen'),
onPressed: () => Navigator.of(context).pushNamed(SearchReservationScreen.routeName),
icon: Icon(Icons.search),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
body: FutureBuilder(
future: _refreshReservations(context),
builder: (context, snapshot) => snapshot.connectionState ==
ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: RefreshIndicator(
onRefresh: () => _refreshReservations(context),
child: Consumer<ReservationProvider>(
builder: (context, reservation, _) => Padding(
padding: EdgeInsets.all(10),
child: ListView.builder(
itemBuilder: (context, index) => Column(
children: <Widget>[
ReservationItem(
id: reservation.reservation[index].id,
customer: reservation.reservation[index].customer,
dateTime: reservation
.reservation[index].performance.dateTime,
movieName: reservation
.reservation[index].performance.movie.title,
),
SizedBox(
height: 10,
)
],
),
itemCount: reservation.reservation.length,
),
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:kinobuchungssystem/src/provider/movie_provider.dart';
import 'package:kinobuchungssystem/src/provider/performance_provider.dart';
import 'package:kinobuchungssystem/src/widgets/select_performance.dart';
import 'package:provider/provider.dart';
class AddPerformanceSelectionScreen extends StatelessWidget {
static const routeName = '/select-performance';
Future<void> _setPerformace(BuildContext context) async {
await Provider.of<PerformanceProvider>(context, listen: false)
.fetchAndSetPerformances();
await Provider.of<MovieProvider>(context, listen: false)
.fetchAndSetMovies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Vorstellung auswählen'),
),
body: FutureBuilder(
future: _setPerformace(context),
builder: (context, snapshot) =>
snapshot.connectionState == ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: RefreshIndicator(
onRefresh: () => _setPerformace(context),
child: Consumer2<PerformanceProvider, MovieProvider>(
builder: (context, performance, movie, _) => Padding(
padding: EdgeInsets.all(10),
child: ListView.builder(
itemCount: performance.performance.length,
itemBuilder: (context, index) {
final loadedMovie = movie.findById(performance.performance[index].movie.id);
return Column(
children: <Widget>[
SelectPerformance(
id: performance.performance[index].id,
movieName: loadedMovie.title,
dateTime: performance.performance[index].dateTime),
Divider(),
],
);
}),
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class SelectPerformance extends StatelessWidget {
final String id;
final String movieName;
final DateTime dateTime;
SelectPerformance({
#required this.id,
#required this.movieName,
#required this.dateTime,
});
#override
Widget build(BuildContext context) {
final time = DateFormat.Hm().format(dateTime);
final date = DateFormat.d().add_MMMM().add_y().format(dateTime);
return Card(
child: ListTile(
onTap: () => Navigator.pop(context, id),
leading: IconButton(
icon: Icon(Icons.add),
onPressed: () {
print(id);
Navigator.pop(context, id);
},
color: Colors.amberAccent,
),
title: Text('$movieName'),
subtitle: Text(date),
trailing: CircleAvatar(
child: Text(time),
radius: 30,
),
),
);
}
}
I suppose it reloads because of the ModalRout.of. Or of the FutureBuilder... But I found no way to fix it.
every setState in the class EditReservationScreen() also the FuturBuilder is calling _getReservation()
and the _newPerformance is updating again with old values
call _getReservation only one time in the initstate, without any futureBuilder
#override
void initState() {
super.initState();
_getReservation();
}
Or you save your updates in the Provider before you call setstate in _updateSeat()

FutureBuilder and async function in flutter

I have a problem with FutureBuilder, it refresh and execute code again when there is a change like when i change value of radio button, i click on radio and it reloads all futurebuilder it seems.
EDIT : i have corrected the problem and here is my solution, i am not sure it works all time
My full code is :
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
// Create a Form widget.
class Affiche_grille extends StatefulWidget {
#override
_Affiche_grille_State createState() {
return _Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> grid;
Future <List<Match>> Grille_display() async {
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': 1};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
for (var u in jsondata) {
Match match = Match(u["equipe1"],u["equipe2"],u["type_prono"]);
Matchs.add(match);
radioValues.add("N");
}
return Matchs;
}
void initState() {
grid = Grille_display();
super.initState();
}
#override
Widget build(BuildContext context) {
final appTitle = 'MONEYFREE';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: Container(
child:
FutureBuilder(
future: grid,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container (
child: Center(
child: Text("Chargement en cours...")
)
);
}
else {
List<Match> values = snapshot.data;
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Libelle Match"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("N"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("2"),
numeric: false,
tooltip: "",
),
],
rows:
List.generate(values.length, (index) {
return DataRow(
cells: [
DataCell(
Text(values[index].equipe1.toString() + " - " + values[index].equipe2.toString()),
),
DataCell(
Radio(
value: "1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print('Change 1');
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print('Change N');
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "2",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print('Change 2');
print(radioValues);
});
},
),
),
]
);
}).toList(),
),
Center(
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
],
)
);
};
},
),
),
),
);
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/valide_grid.php';
var concatenate='';
radioValues.forEach((item){
concatenate=concatenate+item;
});
// Store all data with Param Name.
var data = {'id_membre':1, 'id_grille':1,'result':concatenate};
print (data);
var grille_encode=jsonEncode(data);
print(grille_encode);
// Starting Web API Call.
var response = await http.post(url, body: grille_encode);
print(response.body);
// Getting Server response into variable.
var message = json.decode(response.body);
// If the Response Message is Matched.
if(message == 'OK')
{
print('VALIDATION DE LA GRILLE OK');
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// If Email or Password did not Matched.
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
class Match {
final String equipe1;
final String equipe2;
final String typeprono;
const Match(this.equipe1, this.equipe2, this.typeprono);
}
You can copy paste run full code below
Reason
didUpdateWidget of the FutureBuilder state is being called every time a rebuild is issued. This function checks if the old future object is different from the new one, and if so, refires the FutureBuilder.
https://github.com/flutter/flutter/issues/11426#issuecomment-414047398
Solution
Future _future;
#override
void initState() {
// TODO: implement initState
_future = Grille_display();
}
...
child: FutureBuilder(
future: _future,
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
// Create a Form widget.
class Affiche_grille extends StatefulWidget {
#override
_Affiche_grille_State createState() {
return _Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
List<String> radioValues = [];
Future<List<Match>> Grille_display() async {
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': 1};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
for (var u in jsondata) {
Match match = Match(u["equipe1"], u["equipe2"], u["type_prono"]);
Matchs.add(match);
}
return Matchs;
}
Future _future;
#override
void initState() {
// TODO: implement initState
_future = Grille_display();
}
#override
Widget build(BuildContext context) {
final appTitle = 'MONEYFREE';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: Container(
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(child: Text("Chargement en cours...")));
} else {
List<Match> values = snapshot.data;
values.forEach((m) {
radioValues.add("N");
//like N or something
});
print('valeur radio après initialisation');
print(radioValues);
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Libelle Match"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("N"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("2"),
numeric: false,
tooltip: "",
),
],
rows: List.generate(values.length, (index) {
return DataRow(cells: [
DataCell(
Text(values[index].equipe1.toString() +
" - " +
values[index].equipe2.toString()),
),
DataCell(
Radio(
value: "1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print('Change 1');
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "2",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print(radioValues);
});
},
),
),
]);
}).toList(),
),
Center(
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
],
));
}
;
},
),
),
),
);
}
Future Valide_grille() async {
// For CircularProgressIndicator.
bool visible = false;
// Showing CircularProgressIndicator.
setState(() {
visible = true;
});
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/valide_grid.php';
// Store all data with Param Name.
var data = jsonEncode(radioValues);
print(radioValues);
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var message = json.decode(response.body);
// If the Response Message is Matched.
if (message == 'OK') {
print('VALIDATION DE LA GRILLE OK');
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
} else {
// If Email or Password did not Matched.
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
class Match {
final String equipe1;
final String equipe2;
final String typeprono;
const Match(this.equipe1, this.equipe2, this.typeprono);
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Affiche_grille(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}