I have a problem with my code. I want to control with a checkboxListTitle a list from an http request, as you know in the value: only true or false values are supported, however what I have is a string data type (0 false and 1 True). How could I fix this?, the closest I have been is with this code, but this way it only allows me to check or uncheck, not both at the same time and I can not see the real status of each data.
How could I treat each string in the list as a boolean so as not to lose this logic?
`Future<List> getData() async {
final response = await http.post(Uri.parse(baseurl + 'gettipo.php'),
body: {"categoria": widget.tipo});
return json.decode(response.body);
}
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () async {
setState(() {
getData();
});
},
child: FutureBuilder<List>(
future: getData(),
builder: ((context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ItemList(
list: snapshot.data!,
)
: Center(
child: new CircularProgressIndicator(),
);
}),
),
);
}
}
class ItemList extends StatefulWidget {
final List list;
ItemList({required this.list});
#override
State<ItemList> createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> {
#override
Widget build(BuildContext context) {
value: widget.list[index]['ocultar'] != 0,
onChanged: (val) {
setState(() {
widget.list[index]['ocultar'] = 0;
});`
Related
I am trying to show data from api and while loading data , there should be shown a circularprogressindicator,
but when I start app..it directly showing data instead of circularprogressindicator
class _HomeScreenState extends State<HomeScreen> {
bool isloading = false;
var maplist ;
Future<void> fetchdata() async {
setState(() {
isloading=true;
});
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return isloading==true ? Center(child: CircularProgressIndicator()) : ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
It's actually working perfectly fine, it shows too fast because it is receiving data quickly(+ could be cache case).
If you like to have more delay you can add, future.delay which is unnecessary
Future<void> fetchdata() async {
setState(() {
isloading = true;
});
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
// get more delay
await Future.delayed(Duration(seconds: 2));
setState(() {
isloading = false;
});
}
A better of way of handling future method with FutureBuilder
Try the following code:
class _HomeScreenState extends State<HomeScreen> {
var maplist;
Future<void> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
setState(() {
maplist = json.decode(resp.body);
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return FutureBuilder(
future: fetchdata(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
}
You need to use FutureBuilder, it is not good to use async function in initState, try this:
FutureBuilder<List<Map<String,dynamic>>>(
future: fetchdata(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Map<String,dynamic>> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Text(data[index]['title']));
});
}
}
},
),
also you need to change your fetchdata to this:
Future<List<Map<String,dynamic>>> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
return json.decode(resp.body);
}
Try this one,set isloading default true
class _HomeScreenState extends State<HomeScreen> {
bool isloading = true;
var maplist ;
Future<void> fetchdata() async {
var resp =
await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: MyBody(),
));
}
MyBody() {
return isloading ? Center(child: CircularProgressIndicator()) : ListView.builder(
itemCount: maplist.length,
itemBuilder: (context,index){
return Container(
padding: EdgeInsets.all(8.0),
child: Text(maplist[index]['title']));
});
}
}
You can use like that
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
bool isloading = false;
var maplist;
Future<void> fetchdata() async {
setState(() {
isloading = true;
});
var resp = await http.get(Uri.parse("https://jsonplaceholder.typicode.com/posts"));
maplist = json.decode(resp.body);
setState(() {
isloading = false;
});
}
#override
void initState() {
super.initState();
fetchdata();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: isloading ? const CircularProgressIndicator() : const MyBody(),
);
}
}
class MyBody extends StatelessWidget {
const MyBody({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
//Write your code here
);
}
}
I have a problem with flutter printing out the name and rendering Widget name after running the application
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == true) {
Home();
} else {
return LoginOrSignup();
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body:SafeArea(
child: FutureBuilder(
future: autoLogin(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else {
return LoginOrSignup();
}
}),
))
);
}
}
After running the app the output is LoginOrSignup()
class LoginOrSignup extends StatelessWidget {
const LoginOrSignup({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: MaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
child: Text('Loginsss'),
),
),
Center(
child: MaterialButton(
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Signup()),
);
},
child: Text('Signup'),
),
)
],
),
);
}
}
I have tried using another widget like Text() but it still prints out the same when i run the application on a mobile app. The problem seems to appear in the autoLogin() function that i have
The issue is your future return Widget itself, and when you use Text('${snapshot.data}') it print the widget, To simplfity this you can return data from Future(this is what mostly we do). Let say you like to return widget itself.
A little correction is needed on Future.
Future<Widget> autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == true) {
return Home();
} else {
return LoginOrSignup();
}
}
And
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: FutureBuilder<Widget>(
future: autoLogin(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return snapshot.data!;
} else {
return LoginOrSignup();
}
}),
)));
You are returning a Widget in autoLogin function. Instead you should return a bool.
Future<bool?> autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == null) return null;
if (loggedIn == true) {
return true;
} else {
return false;
}
}
Then in the FutueBuilder you can check if it's then return Home()
if (snapshot.hasData && snapshot.data! == true) {
return Home();
} else {
return LoginOrSignup();
i am new to flutter and trying to display data from a http post
referencing from [1]https://flutter.dev/docs/cookbook/networking/background-parsing and [2]https://flutter.dev/docs/cookbook/networking/fetch-data
i tried to display data on a futurebuilder but it keeps displaying this from the Text('${snapshot.data}')
[Instance of 'DashBoardBanner', Instance of 'DashBoardBanner', Instance of 'DashBoardBanner']
Builder
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<List<DashBoardBanner>> futureBanner;
#override
void initState() {
super.initState();
futureBanner = getBannerDataFromServer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [
Card(
child: FutureBuilder(
future: getBannerDataFromServer(),
builder: (context,snapshot){
if(snapshot.connectionState == ConnectionState.done){
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
}
return const CircularProgressIndicator();
},
),
)
],
)),
);
}
}
Class and postreq
class DashBoardBanner {
final String MsgId;
final String MsgKey;
final String MsgPic;
const DashBoardBanner(
{required this.MsgId, required this.MsgKey, required this.MsgPic});
factory DashBoardBanner.fromJson(Map<String, dynamic> json) {
return DashBoardBanner(
MsgId: json['MsgId'] as String,
MsgKey: json['MsgKey'] as String,
MsgPic: json['MsgPic'] as String,
);
}
}
Future<List<DashBoardBanner>> getBannerDataFromServer() async {
final queryParameters = {
"ApiFunc": 'Banner',
"UserKey": getDeviceKey(),
"Token": getDeviceToken(),
"SubmitContent": json.encode({"MobileNo": getMobileNo1()})
};
final response = await http.post(
Uri.http('somesite.net', '/capi.aspx', queryParameters),
);
if (response.statusCode == 200) {
Map<String, dynamic> data = jsonDecode(response.body);
final splitoff = jsonEncode(data['RespContent']);
return compute(parseBanner, splitoff);
} else {
throw Exception('Failed to load Data');
}
}
List<DashBoardBanner> parseBanner(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<DashBoardBanner>((json) => DashBoardBanner.fromJson(json))
.toList();
}
Edit : i rebuilt the file replicating reference[1] and it finally displayed the data i needed, it seems the issue stem from not having this 2nd widget which return the obj back , however how do i combine the 2nd build widget into the first without needing the whole widget as having a whole build widget to return 1 line seems pointless?
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body:Container(
child: FutureBuilder<List<DashBoardBanner>>(
future: getBannerDataFromServer(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
print(snapshot.data!.length);
return DashBoardBannersList(dashboardBanners: snapshot.data!); <--- original issue due to not having this
} else {
return CircularProgressIndicator();
}
},
),
),
);
}
}
class DashBoardBannersList extends StatelessWidget {
const DashBoardBannersList({Key? key, required this.dashboardBanners}) : super(key: key);
final List<DashBoardBanner> dashboardBanners;
#override
Widget build(BuildContext context) {
return Text(dashboardBanners[0].MsgId);
}
}
This error is caused because of the sound null safety
snapshot.data might be null for some requests so you can't access the array at a certain index cause it can be null.
If you know for sure snapshot.data exists you can use the ! operator to tell dart the variable is not null for sure like that:
snapshot.data![index];
You can also check if the data is null before accessing it like that:
if (snapshot.data != null) {
// do something with snapshot.data[index]
}
I recommed to read more about sound null safety here
Check the Firestore docs.
Inside snapshot.data, there's docs (every document of your collection).
The code is from there:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
The code above shows how to convert every doc (type DocumentSnapshot) to a JSON format (that can be represented with Map<String, dynamic>). To access to the doc id, you'll access with document.id, because it isn't inside the document.data() method.
You wanna retrieve a list of DashBoardBanner but you forget initialize the futurebuilder by adding a ListView.builder().
Try to use the following code idea :
FutureBuilder(
future: getBannerDataFromServer(http.Client()),
builder: (context, AsyncSnapshot snapshot) {
print(snapshot.hasData);
if (snapshot.hasError) {
return CircularProgressIndicator();
} else if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
var data = snapshot.data![index];
return DashBoardBannersList(dashboardBanners: data);
},),
),},
},)
I created a general ListPage as below:
class ListPage extends StatelessWidget {
final context;
final Future<Either<Response, List<dynamic>>> futureFuncion;
final classListItem, classDetailPage;
const ListPage(this.context,this.futureFuncion,this.classListItem,this.classDetailPage, {Key key,}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10.0),
child: Column(children: [Expanded(child: _getData())]));
}
FutureBuilder _getData() {
return FutureBuilder<Either<ResponseError, List>>(
future: futureFuncion,
builder: (BuildContext context, AsyncSnapshot<Either<ResponseError, List>> snapshot) {
if (snapshot.hasError) {
return globals.showSnapshotError(snapshot);
} else if (!snapshot.hasData) {
return globals.showLoading('Loading...');
} else {
return snapshot.data.fold((l) {
return Error().showError(l.error, l.errorDescription);
}, (r) {
return ListView(children: _buildList(context, r));
});
}
},
);
}
}
I use code as below to create List Page:
class RequestListPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
String url = globals.debug ? 'assets/data/list.json' : 'domain.com/list';
return Scaffold(
appBar: AppBar(toolbarHeight: 50,centerTitle: true,title: Text("List")),
body: ListPage(
context,
debug
? LoadDataFromJsonFile().loadListData(context, url, '$Response')
: RequestApi().getListData(url, '$Response'),
RequestListItem,
RequestDetailPage,
),
);
}
Widget _buildList(context, list) {
List<Widget> children = [];
list.forEach((item) {
children.add(_buildTile(context, item));
});
return ListView(children: children);
}
ListTile _buildTile(context, item) {
return ListTile(
title: _buildListItem(context, item),
onTap: () => showDetail(context, item),
);
}
Widget _buildListItem(context, item) {
return classListItem;
}
void showDetail(context, item) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => classDetailPage(context, item)),
);
}
}
}
When I run this code, I got error as below, what can I do?
Attempted to use type 'RequestListItem' as a function. Since types do not define a method 'call', this is not possible. Did you intend to call the RequestListItem constructor and forget the 'new' operator?
Receiver: RequestListItem
Tried calling: RequestListItem(Instance of 'StatefulElement', Instance of 'Response')
I am trying to update the value of totalPricewith the value that comes from the response from API. I have created a currentTotal methods that contains setState(). Then passed snapshot.data.price.totalAmountvalue to currentTotal in order to update the value of totalPrice.But, it doesnt update the value. Can you help?
double totalPrice = 0;
#override
Widget build(BuildContext context) {
currentTotal(double x) {
setState(() {
totalPrice += x;
});
}
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: FutureBuilder<SearchResult>(
future: serviceOne.postCall(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data != null) {
return new Material(
child: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
ListTile(
title: new Text(totalPrice.toString()),
)
]),
),
]
)
}
currentTotal(snapshot.data.price.totalAmount);
else if (snapshot.hasError) {
return Text("error....${snapshot.error}");
}
There are many things needs to be fixed in your build.
1 - Your widget is StatefulWidget, to use FutureBuilder inside StatefulWidget read this:
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
Briefly, create Future future; instance field, then assign it inside the initState and use that future for FutureBuilder.
2 - your setState not inside a method, you have probably syntax error there. Create a void method and use setState inside it.
3 - You don't need to check twice like:
if (snapshot.hasData) {
if (snapshot.data != null) {
One of them enough, after the condition check, call your method includes setState, then display it.
Edit:
Here an example template for your solution:
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
Future<int> future;
int price = 0;
#override
void initState() {
future = fetchPrice();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder<int>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text(price.toString()),
);
}
return Center(child: CircularProgressIndicator());
},
),
),
);
}
Future<int> fetchPrice() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/posts/1');
final data = json.decode(response.body);
setState(() {
price = data['userId'];
});
return data['userId'];
}
}