Creating dynamic list(request), I put a switch in there.
However, the switches refuse to show their intended state (The status does not change when clicked.)
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scat/request.dart' as request;
import 'package:scat/util.dart' as util;
class _ConfigPushState extends State<ConfigPush> {
Future<request.ApiResult> configList;
Map<String, bool> _onOffMap = {};
#override
void initState() {
super.initState();
configList = request.configList();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0), // here the desired height
child: new AppBar(
backgroundColor: Colors.black,
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
centerTitle: true,
title: new Text('API 설정', style: util.appTitleStyle),
),
),
body: Center(
child: FutureBuilder<request.ApiResult>(
future: configList,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return CircularProgressIndicator();
}
_onOffMap = {};
return ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: snapshot.data.data.length,
itemBuilder: (context, index) {
var row = snapshot.data.data[index];
var type = row['type'] as String;
var value = row['value'] as bool;
var disabled = row['isDisabled'] as bool;
var subtitle = disabled ? '아직 준비중' : row['description'];
_onOffMap[type] = value;
// _arr.add(value);
// _lights = value;
print('item builder ${type}');
return Container(
height: util.isEmpty(subtitle) ? 50 : 70,
child: new ListTile(
title: new Text(row['name']),
subtitle: new Text(subtitle),
trailing: CupertinoSwitch(
activeColor: Colors.deepPurple,
// value: _arr[index],
// value: _lights,
value: _onOffMap[type],
onChanged: (bool value) {
setState(() {
if (disabled) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('개발중..')));
return;
}
// _lights = value;
print("before $_onOffMap $type");
_onOffMap[type] = value;
print("after $_onOffMap");
request
.pushSet(type, value.toString(), "", "")
.then((a) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('처리 되었습니다.')));
});
});
},
),
));
});
})),
);
}
}
try this one I hope it work for you:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scat/request.dart' as request;
import 'package:scat/util.dart' as util;
class _ConfigPushState extends State<ConfigPush> {
Future<request.ApiResult> configList;
Map<String, bool> _onOffMap = {};
#override
void initState() {
super.initState();
configList = request.configList();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(50.0), // here the desired height
child: new AppBar(
backgroundColor: Colors.black,
leading: new IconButton(
icon: new Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
centerTitle: true,
title: new Text('API 설정', style: util.appTitleStyle),
),
),
body: Center(
child: FutureBuilder<request.ApiResult>(
future: configList,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return CircularProgressIndicator();
}
_onOffMap = {
"type": true;
};
return ListView.separated(
separatorBuilder: (context, index) => Divider(),
itemCount: snapshot.data.data.length,
itemBuilder: (context, index) {
var row = snapshot.data.data[index];
var type = row['type'] as String;
var value = row['value'] as bool;
var disabled = row['isDisabled'] as bool;
var subtitle = disabled ? '아직 준비중' : row['description'];
_onOffMap["type"] = value;
// _arr.add(value);
// _lights = value;
print('item builder ${type}');
return Container(
height: util.isEmpty(subtitle) ? 50 : 70,
child: new ListTile(
title: new Text(row['name']),
subtitle: new Text(subtitle),
trailing: CupertinoSwitch(
activeColor: Colors.deepPurple,
// value: _arr[index],
// value: _lights,
value: _onOffMap["type"],
onChanged: (bool value) {
setState(() {
if (disabled) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('개발중..')));
return;
}
// _lights = value;
print("before $_onOffMap $type");
_onOffMap["type"] = value;
print("after $_onOffMap");
request
.pushSet(type, value.toString(), "", "")
.then((a) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('처리 되었습니다.')));
});
});
},
),
));
});
})),
);
}
}
Related
I set up flutter_map succesfully, but when I try to filter my map by "City" for example I am getting this error:
The following LateError was thrown building FutureBuilder<List<dynamic>>(dependencies: [MediaQuery],
state: _FutureBuilderState<List<dynamic>>#cb20d):
LateInitializationError: Field '_state' has already been initialized.
The relevant error-causing widget was:
FutureBuilder<List<dynamic>>
My flutter_map implementation is as follow:
late MapController mapController;
Future<List<dynamic>>? futureLocs;
Future<List<dynamic>>? futureLocsFilteredByCity;
bool? isFilterByCity;
PageController pageController = PageController();
double currentZoom = 10.0;
PanelController panelController = PanelController();
#override
void initState() {
super.initState();
mapController = MapController();
pageController = PageController(viewportFraction: 0.7, initialPage: 0);
futureLocs = getAllDogsLocation();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: GenericAppBar(context,
backbutton: true,
title: 'Dogs map',
filterbutton: true, onfilterpress: () {
showDialog(
context: context,
builder: (context) {
return CitiesToFilter(
futureLocs: futureLocs,
onCityPress: (city) {
setState(() {
isFilterByCity = true;
futureLocs = getDogLocationByCity(city);
futureLocs!.then((value) {
if (value.isNotEmpty) {
var latlong = LatLng(
value[0]['latitude'], value[0]['longitude']);
widget.lat = latlong.latitude;
widget.long = latlong.longitude;
}
});
});
});
});
}),
body: FlutterMapCusto(
futureLocs: futureLocs,
mapController: mapController,
pageController: pageController,
lat: widget.lat,
long: widget.long,
panelcontroller: panelController,
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
heroTag: Text('CurrentLoc'),
onPressed: () {
setState(() {
mapController.move(
LatLng(widget.lat, widget.long), currentZoom);
});
},
tooltip: 'Current location',
child: const Icon(Icons.location_history),
),
],
));
}
}
where FlutterMapCusto widget is defined as a normal widget with FlutterMap class. I am not including it to avoid boilerplate code here since it is a basic implementation found in the package web. I think the error is coming from mapController..
On the other hand I am fetching my new data filtered by city with the function "getDogLocationByCity(city)" updating my future.
Then we have CitiesToFilter widget:
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Filter'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Filter by City'),
FloatingActionButton(onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Cities:'),
content: SizedBox(
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: widget.futureLocs,
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Text('Loading...');
case ConnectionState.active:
{
return const Center(
child: Text('Loading...'),
);
}
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'Error: ${snapshot.error}');
}
if (snapshot.hasData) {
return ListView.builder(
itemCount:
snapshot.data!.length,
itemBuilder: (context, index) {
return TextButton(
onPressed: () {
setState(() {
widget.onCityPress( snapshot.data![index]['CityName'] );
});
Navigator.pop(context);
},
child: Text(
snapshot.data![index]
['CityName']));
});
} else {
return const Text(
'No data available');
}
}
},
),
),
);
});
})
],
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Close'))
],
);
}
Future method to fetch the data shown in map. This is just a wrapper developed from Back4App to interact with its MongoDb database:
Future<List<dynamic>> getAllDogsLocation() async {
await Future.delayed(const Duration(seconds: 2), () {});
QueryBuilder<ParseObject> queryTodo =
QueryBuilder<ParseObject>(ParseObject('Todo'));
// queryTodo.includeObject(['latitude']);
final ParseResponse apiResponse = await queryTodo.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results as List<ParseObject>;
} else {
throw Exception('Failed to load data');
}
}
I'm reading a list from my local Json file and i'm trying to sort the list by either number or alphabet and update the UI depend on user choice.
I'm able to filter the List but not really sure how to update the UI when a user press a either button so I would be really appreciated if I can get any help or suggestion.
Right now, I just called one function in my FutureBuilder and not sure how to modify it.
class _SawnawkScreenState extends State<SawnawkScreen> {
#override
Widget build(BuildContext context) {
bool isSwitched = false;
return Scaffold(
body: FutureBuilder(
future: SortbyNumber(), // Need to do something here
builder: (context, data) {
if (data.hasError) {
return Center(child: Text("${data.error}"));
} else if (data.hasData) {
var items = data.data as List<SawnAwkModel>;
return ListView.builder(
itemCount: items == null ? 0 : items.length,
itemBuilder: (context, index) {
return SawnawkCardWidget(
id: items[index].id!,
);
});
} else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: SpeedDial(
children: [
SpeedDialChild(
child: Icon(Icons.sort_by_alpha_outlined),
backgroundColor: Colors.white,
label: 'Sort by alphabet',
onTap: () => {
print('sort by alphabet'),
//Do something here
}),
SpeedDialChild(
child: Icon(Icons.sort_by_number),
backgroundColor: Colors.white,
label: 'Sort by number',
onTap: () => {
print('sort by number'),
//Do something here
}),
],
),
);
}
}
Future<List<SawnAwkModel>> SortbyNumber() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
return list.map((e) => SawnAwkModel.fromJson(e)).toList();
}
Future<List<SawnAwkModel>> SortbyAlphabet() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
List<SawnAwkModel> profileList =
list.map((e) => SawnAwkModel.fromJson(e)).toList();
profileList.sort((a, b) {
return a.titleFalam.toLowerCase().compareTo(b.titleFalam.toLowerCase());
});
return profileList;
}
In order to update the UI, the code that changes the UI must be in a setState({}) function. In your case, try this:
SpeedDialChild(
child: Icon(Icons.sort_by_alpha_outlined),
backgroundColor: Colors.white,
label: 'Sort by alphabet',
onTap: () => {
print('sort by alphabet'),
setState({
final sorted = await SortbyAlphabet()
//update widget contents with sorted value above
})
}),
Your current code if difficult to update the UI, I suggest storing the ListView.builder items in a variable accessible by the function you want to use to update the UI, and change the contents there, like this:
class _SawnawkScreenState extends State<SawnawkScreen> {
bool isSwitched = false;
List items = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: SortbyNumber(), // Need to do something here
builder: (context, data) {
if (data.hasError) {
return Center(child: Text("${data.error}"));
} else if (data.hasData) {
items.addAll(data.data as List<SawnAwkModel>);
return ListView.builder(
itemCount: items == null ? 0 : items.length,
itemBuilder: (context, index) {
return SawnawkCardWidget(
id: items[index].id!,
);
});
} else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: SpeedDial(
children: [
SpeedDialChild(
child: Icon(Icons.sort_by_alpha_outlined),
backgroundColor: Colors.white,
label: 'Sort by alphabet',
onTap: () async {
setState({
print('sort by alphabet'),
final newItems = await SortbyAlphabet();
items.clear();
items.addAll(newItems);
})
}),
SpeedDialChild(
child: Icon(Icons.sort_by_number),
backgroundColor: Colors.white,
label: 'Sort by number',
onTap: () async {
setState({
print('sort by number'),
final newItems = await SortbyNumber();
items.clear();
items.addAll(newItems);
})
}),
],
),
);
}
}
Future<List<SawnAwkModel>> SortbyNumber() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
return list.map((e) => SawnAwkModel.fromJson(e)).toList();
}
Future<List<SawnAwkModel>> SortbyAlphabet() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
List<SawnAwkModel> profileList =
list.map((e) => SawnAwkModel.fromJson(e)).toList();
profileList.sort((a, b) {
return a.titleFalam.toLowerCase().compareTo(b.titleFalam.toLowerCase());
});
return profileList;
}
Please refer to this https://stackoverflow.com/a/70202810/15215450 for example on ValueListenable Builder
Please refer to the below code
final ValueNotifier<List> items = ValueNotifier([]);
floatingActionButton: SpeedDial(
children: [
SpeedDialChild(
child: Icon(Icons.sort_by_alpha_outlined),
backgroundColor: Colors.white,
label: 'Sort by alphabet',
onTap: () => {
print('sort by alphabet'),
//Do something here
items.value.clear();
items.value = await SortbyAlphabet();
items.notifyListeners();
}),
SpeedDialChild(
child: Icon(Icons.sort_by_number),
backgroundColor: Colors.white,
label: 'Sort by number',
onTap: () => {
print('sort by number'),
//Do something here
items.value.clear();
items.value = await SortbyAlphabet();
items.notifyListeners();
}),
],
),
ValueListenableBuilder(
valueListenable: isSwitched,
builder: (context, snapshot, child) {
return ListView.builder(
itemCount: items.value == null ? 0 : items.value.length,
itemBuilder: (context, index) {
return SawnawkCardWidget(
id: items.value[index].id!,
);
});
}));
Try this
late Future<dynamic> _future;
#override
void initState() {
_future = getDoctors();
}
class _SawnawkScreenState extends State<SawnawkScreen> {
#override
Widget build(BuildContext context) {
bool isSwitched = false;
return Scaffold(
body: FutureBuilder(
future: _future, // Need to do something here
builder: (context, data) {
if (data.hasError) {
return Center(child: Text("${data.error}"));
} else if (data.hasData) {
var items = data.data as List<SawnAwkModel>;
return ListView.builder(
itemCount: items == null ? 0 : items.length,
itemBuilder: (context, index) {
return SawnawkCardWidget(
id: items[index].id!,
);
});
} else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: SpeedDial(
children: [
SpeedDialChild(
child: Icon(Icons.sort_by_alpha_outlined),
backgroundColor: Colors.white,
label: 'Sort by alphabet',
onTap: () => {
print('sort by alphabet'),
//Do something here
setState(() {. // call setstate to refresh futurebuilder
_future = SortbyAlphabet();
}),
}),
SpeedDialChild(
child: Icon(Icons.sort_by_number),
backgroundColor: Colors.white,
label: 'Sort by number',
onTap: () => {
print('sort by number'),
//Do something here
}),
],
),
);
}
}
Future<List<SawnAwkModel>> SortbyNumber() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
return list.map((e) => SawnAwkModel.fromJson(e)).toList();
}
Future<List<SawnAwkModel>> SortbyAlphabet() async {
final jsondata =
await rootBundle.rootBundle.loadString('assets/data/sawnawk_data.json');
final list = json.decode(jsondata) as List<dynamic>;
List<SawnAwkModel> profileList =
list.map((e) => SawnAwkModel.fromJson(e)).toList();
profileList.sort((a, b) {
return a.titleFalam.toLowerCase().compareTo(b.titleFalam.toLowerCase());
});
return profileList;
}
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,
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),
),
);
}
}
Here I've one service page where I've displayed all the services, and there I can select multiple services. but I want to select only one service. I've used the checkbox Listitle for the service selection. I want that user can select only one service not multiple services at a time.
Here is code i've tried :
class _AddWalkinServiceScreenState extends State<AddWalkinServiceScreen>
with TickerProviderStateMixin {
List<int> servicesIds = [];
int selected = 0;
Map<String, bool> _selection = {};
List<BspServices.Service> selectedServices = [];
SearchBarController _controller = new SearchBarController();
String _searchText = '';
List<dynamic> finalList = new List();
List<dynamic> searchList = new List();
bool isLoading = false;
AnimationController controller;
Animation<double> animation;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeInQuint);
controller.forward();
}
Widget _renderServices(AddWalkinServiceViewModel awsVm) {
List lovCountryServices = searchList.length != 0 ? searchList : finalList;
if (lovCountryServices == null || lovCountryServices.length == 0) {
return Container(
child: Center(
child: Text("No Services available for this combination"),
),
);
}
// print(lovCountryServices);
return Container(
child: finalList.length < 1
? ListTile(
leading: CircularProgressIndicator(),
)
: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item = lovCountryServices[
index]; // should be outside build function
List items = item['services'];
return ExpansionTile(
title: Text(item['name']),
children: List.generate(items.length, (i) {
_selection[items[i]['name']] =
_selection[items[i]['name']] ?? items[i]['isSelected'];
return CheckboxListTile(
title: Text(items[i]['name']),
value: _selection[items[i]['name']] == null
? false
: _selection[items[i]['name']],
onChanged: (val) {
setState(() {
_selection[items[i]['name']] = val;
if (val) {
servicesIds.add(items[i]['id']);
List<BspServices.Service> services =
selectedServices.where((service) {
return service.mainCategory == item['name'];
}).toList();
SubCategory subService = new SubCategory(
id: items[i]['id'],
name: items[i]['name'],
);
List<SubCategory> subCategories = [];
if (services.length < 1) {
subCategories.add(subService);
selectedServices.add(
new BspServices.Service(
mainCategory: item['name'],
mainCategoryId: item['id'],
subCategory: subCategories,
),
);
} else {
print('services in else');
print(services[0].subCategory);
subCategories = services[0].subCategory;
subCategories.add(subService);
}
} else {
servicesIds.removeWhere((service) {
return service == items[i]['id'];
});
List<BspServices.Service> services =
selectedServices.where((service) {
return service.mainCategory == item['name'];
}).toList();
services[0].subCategory.removeWhere((subService) {
return subService.id == items[i]['id'];
});
}
});
print('servicesIds after set state');
print(servicesIds);
},
);
}),
);
},
),
);
}
Widget content(BuildContext context, AddWalkinServiceViewModel awsVm) {
Orientation orientation = MediaQuery.of(context).orientation;
var colorStyles = Theming.colorstyle(context);
final appBar = SearchBar(
controller: _controller,
onQueryChanged: (String query) {
print('Search Query $query');
setState(() {
_searchText = query;
});
_searchFilter();
},
defaultBar: AppBar(
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
// NavigationHelper.navigatetoBack(context);
}),
title: Text('Select Services'),
),
);
return new Scaffold(
backgroundColor: colorStyles['primary'],
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: FadeTransition(
opacity: animation,
child: new Container(
margin: EdgeInsets.only(top: 10),
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(50.0),
topRight: const Radius.circular(50.0),
),
),
child: isLoading ? FadeInUi() : _renderServices(awsVm),
),
),
);
}
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, AddWalkinServiceViewModel>(
converter: (Store<AppState> store) =>
AddWalkinServiceViewModel.fromStore(store),
onInit: (Store<AppState> store) {
print('store.state.servicesState.servicesByCountry');
print(store
.state.servicesState.servicesByCountry.servicesByCountry[0].name);
Map<String, dynamic> services =
store.state.servicesState.servicesByCountry.toJson();
finalList = services['servicesByCountry'];
print('finalList = $finalList');
},
builder: (BuildContext context, AddWalkinServiceViewModel awsVm) =>
content(context, awsVm),
);
}
}
UPDATED
List<Map> services = [];
List<int> selections = [];
#override
void initState() {
super.initState();
getList();
}
void getList() async {
//get data from internet/api
//for ex. I m using offline data
setState(() {
services = List.generate(
10,
(ind) => {
'name': 'Service Category $ind',
'services': ['Service 1', 'Service 2']
}).toList();
selections = List.generate(10, (ind) => -1).toList();
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
color: Colors.white,
child: services.length < 1
? ListTile(
leading: CircularProgressIndicator(), title: Text('Loading...'))
: ListView.builder(
itemCount: services.length,
itemBuilder: (con, ind) {
return ExpansionTile(
title: Text('${services[ind]['name']}',
style: TextStyle(color: Colors.black)),
children:
List.generate(services[ind]['services'].length, (ii) {
return CheckboxListTile(
title: Text('${services[ind]['services'][ii]}',
style: TextStyle(color: Colors.green[900])),
value: selections[ind] == ii,
onChanged: (b) {
setState(() {
selections[ind] = ii;
});
});
}).toList());
}));
}