Filter ListView with FutureBuilder? - json

How can I upgrade this code into searching listview?
I try with using two lists and using where keyword but it not worked. some problem with filling data into a list, then printing the list it always shows Instance of User
class ListBuilder extends StatefulWidget {
final FetchList fetchList;
ListBuilder({required this.fetchList});
#override
_ListBuilderState createState() => _ListBuilderState();
}
class _ListBuilderState extends State<ListBuilder> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: widget.fetchList.getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return LoadingView();
} else {
if (snapshot.data.length > 0) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: TextField(
// onChanged: onItemChanged,
// controller: _textController,
decoration: InputDecoration(
hintText: 'Search Here by EmployeeId...',
),
),
),
Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return UserListTile(user: snapshot.data[index]);
},
),
),
],
);
} else {
return EmptyView();
}
}
},
);
}
}
Here this list view build by using JSON data and using flutter FutureBuilder.

I would suggest doing such things with a proper state management. While you can certainly achieve this within the widget, it will end with a poorly designed app. Have a look at this brief tutorial on a todo list with filters using BLoC. Then there is a filtered list example which reads data from the network. I believe this makes more sense and is not so hard to learn

Related

Flutter Filtering Future with list builder

I have a widget that is meant to return a list/listtile of amenities in a location,
the data comes from a JSON file which I get when the page loads and displays a list of locations. the user then clicks on a location and gets a list of amenities in said location. can we do something like
tmp = amenities.filter(el => el.locationid=locationid
class _Locations extends State<Locations>
with SingleTickerProviderStateMixin {
late Future<Amenities> amenities;
#override
void initState() {
super.initState();
amenities = AmenitiesDataApi.getAmenities();
}
Widget localAttractionsTab(locationid) {
return Column(
children: <Widget>[
FutureBuilder(
future: amenities,
builder: (BuildContext context, AsyncSnapshot<Amenities> snapshot) {
if (snapshot.hasData) {
for (var amen in snapshot.data!.amenities) {
if (amen.locationid == locationid) {
return ListTile(Text(snapshot.data!.amenities[0].amenityname),);
}
}
throw ('error');
}
},
),
],
);
}
That should be possible, but you need to re-arrange your widget a litte.
The Column wants to see a <Widget>[] for its child parameter, so you can use filter and map on the list here:
Widget localAttractionsTab(locationid) {
return FutureBuilder(
future: amenities,
builder: (BuildContext context, AsyncSnapshot<Amenities> snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data!.amenities
.where((el) => el.locationid == locationid)
.map((el) => ListTile(Text(el.amenityname)))
.toList()
);
}
return Container();
},
);
}
You can try this
Widget localAttractionsTab(locationid) {
return FutureBuilder(
future: amenities,
builder: (BuildContext context, AsyncSnapshot<Amenities>
snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data!.amenities.where((amen) => amen.locationid ==
locationid)
.map((amen) => ListTile(Text(amen.amenityname),)
).toList());
}
return CircularProgressIndicator();
},
);
}
You can also update your code by moving the Column into the FutureBuilder and also do the filtration outside the for loop.
Widget localAttractionsTab(locationid) {
return FutureBuilder(
future: amenities,
builder: (BuildContext context, AsyncSnapshot<Amenities> snapshot) {
return Column(
children: <Widget>[
if (snapshot.hasData) {
for (var amen in snapshot.data!.amenities.where((amen)=> amen.locationid == locationid)
ListTile(Text(amen.amenityname))
}
]);
}),
);
}

How to convert MQTT Message to JSON format in Flutter

I already connect and get data from MQTT broker using snapshot method. However, I want to convert the MQTT message to JSON format cause I want to do some condition code to certain data.
My problem is when I stream data from the MQTT broker, my data only came out for the last data for that index. That index consists three different data, but since the index hold 3 data at the same time, only the last data of the index that displayed. This is my current code. How can I do that?
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flutter MQTT"),
),
body: FutureBuilder(
future: mqttSubscribe(topic: "/sensor_simulator/data"),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Center(
child: Text("Error: ${snapshot.error}"),
);
}
// if succeed to connect
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder(
stream: snapshot.data,
builder: (BuildContext context, AsyncSnapshot snapshot) {
// if have error--> display
if (snapshot.hasError) {
return Center(
child: Text("Error: ${snapshot.error}"),
);
}
// if data detected--> display
if (snapshot.hasData) {
try {
final List<MqttReceivedMessage> receiveMessage =
snapshot.data;
final recMess =
receiveMessage[0].payload as MqttPublishMessage;
String payload = MqttPublishPayload.bytesToStringAsString(
recMess.payload.message);
return ListView(
children: [
Card(
child: ListTile(
title: Text(payload),
))
],
padding: EdgeInsets.all(10),
);
} catch (e) {
return Center(
child: Text("Error: ${e.toString()}"),
);
}
}
return Center(child: CircularProgressIndicator());
},
);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
}
You can use this piece of code:
void _subscribeToTopic(String topicName) {
print('MQTTClientWrapper::Subscribing to the $topicName topic');
client.subscribe(topicName, MqttQos.atMostOnce);
client.updates!.listen((List<MqttReceivedMessage<MqttMessage>> c) {
final recMess = c[0].payload as MqttPublishMessage;
String message =
MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
String decodeMessage = Utf8Decoder().convert(message.codeUnits);
print("MQTTClientWrapper::GOT A NEW MESSAGE $decodeMessage");
});
}

How to keep precedent widgets after setState

I want to create a dynamic listview of cards I create with a title and a content. I'm decoding what I receive from the two textfields of the adding page but then when a card is created, it doesn't work well. For the first card, it works perfectly, but when a second one is created, the two cards are the same. I don't really know how to keep my precedent cards while adding new one. If you have any ideas or tips please tell me. Thank you in advance! Here's my code :
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
static List dreams = [];
static List contentCard = [];
String text;
String title;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kEdgeColor,
appBar: AppBar(
backgroundColor: kEdgeColor,
elevation: 0,
title: Text('My dreams'),
),
body: Container(
decoration: BoxDecoration(
color: Colors.black),
child: ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
return new DreamCard(
Content: text,
title: title,
);
}
)
),
bottomNavigationBar: BottomAppBar(
color: kEdgeColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlatButton(
onPressed: (){
Navigator.popAndPushNamed(context, '/public');
},
child: Icon(Icons.public),
color: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
),
FlatButton(
onPressed: (){
Navigator.popAndPushNamed(context, '/');
},
child: Icon(Icons.bedtime),
color: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: () async {
final result = await Navigator.of(context).push(MaterialPageRoute(builder: (context){return WritingPage();}));
if (null != result) {
dreams.add(result);
Map<String, dynamic> resultat = jsonDecode(result);
setState(() {text = resultat['text'];
title = resultat['title'];});
}
},
child: Icon(Icons.add, size: 30,color: Colors.black,),
backgroundColor: Colors.white,
),
);
}
}
cause you're only update the title and text when press, and your DreamCard has the same title and text, since you'r not reference items in dreams list by index. so your DreamCard in list always look the same.
if (null != result) {
Map<String, dynamic> resultat = jsonDecode(result);
// use setState to update your dreams list to trigger UI change
setState(() {
dreams.add(resultat);
});
}
ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
// access item from dreams list by index
return new DreamCard(
Content: dreams[index]['text'],
title: dreams[index]['title],
);
}
)
Your itemBuilder is not referencing index in:
child: ListView.builder(
itemCount: dreams.length ,
itemBuilder: (BuildContext ctxt, int index) {
return new DreamCard(
Content: text,
title: title,
);
}
so of course all cards will always has the same text and title as whatever is in scope. You need to put the data in dreams and reference dreams[index][?something?] to get the two values appropriate for this card.
You cannot see the correct text for the next DreamCard added because you are adding the result received from WritingPage page directly to the dreams list. You are then JSON decoding result and setting the values of variables text and title. These values of the latest WritingPage are sent to DreamCard therefore every DreamCard appears to be the same. Instead you should be JSON decoding the dreams list and using the text and title from dreams list. Please make the following changes.
return new DreamCard(
Content: jsonDecode(dreams[index])["text"],
title: jsonDecode(dreams[index])["title"],
);

infinite loading for json data in flutter

EDIT - "When I tried to run print(snapshot.error), It gave "type int is not a subtype of type string""
I am trying to get json data from https://raw.githubusercontent.com/RahulBagdiOfficial/rto_app_flutter/master/assets/json/applyonline.json
using https request package then parsing it into json data,
I am using it to build a list using ListView.builder
that if the data is null return CircularProgressIndicator
and if it contain data return list
The problem is This
its Stuck on loading
This is my code
class ApplyOnline extends StatefulWidget {
#override
_ApplyOnlineState createState() => _ApplyOnlineState();
}
class _ApplyOnlineState extends State<ApplyOnline> {
#override
Future<List<ApplyOnlineList>> _getapplyonlinelist() async {
var data = await http.get(
"https://raw.githubusercontent.com/RahulBagdiOfficial/rto_app_flutter/master/assets/json/applyonline.json");
var jsonData = json.decode(data.body);
List<ApplyOnlineList> applyonlinelist = [];
for (var i in jsonData) {
ApplyOnlineList applyonlineobject =
ApplyOnlineList(i['index'], i['string'], i['url']);
applyonlinelist.add(applyonlineobject);
}
print(applyonlinelist.length);
return applyonlinelist;
}
Widget customURLButton(String text, String URL, Icon icon) {
;
}
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xff655ee6),
appBar: AppBar(
backgroundColor: Color(0xff655ee6),
title: Text("Apply Online"),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: FutureBuilder(
future: _getapplyonlinelist(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} if(snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].string),
);
},
);
}
},
),
),
),
);
}
}
class ApplyOnlineList {
final int index;
final String url;
final String string;
ApplyOnlineList(this.url, this.index, this.string);
}
You should pass reference of the future since you're not accepting any params in future:
future: _getapplyonlinelist
Check all the connection state before getting into snapshot.
And for checking snapshot, You can do this way:
if(snapshot.hasData) {
// return something
} else if(snapshot.hasError) {
// play with error
}
return CircularProgressIndicator();
The problem is, you're checking for null, and returning the widget, doing this doesn't allow the FutureBuilder to rebuild because you're not checking its connection state, so the state of the data won't update. Try this instead.
...
FutureBuilder(
future: _getapplyonlinelist(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting || !snapshot.hasData)
{
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].string),
);
);
}
...
Checking the ConnectionState will let the future builder resolve the future properly.
future: _getapplyonlinelist(),
Don't do this. This way, every time your build function is called, your Future will start over. You need to start it once and then wait for it.
In your state, outside the build method, have a variable to hold the future. Assign _getapplyonlinelist() to this variable once, probably in the void initState () method. Then use that variable in your build method. That way, no matter how often the build method is called, it will not start the Future over and over and over.
In your state class:
Future<List<ApplyOnlineList>> waitingForOnlineList;
void initState () {
waitingForOnlineList = _getapplyonlinelist();
}
... and then in your build method:
future: waitingForOnlineList,

Flutter transferring Json data to another page

Hey I have the problem that I want to transfer the data from a ListTile which was pressed. So i can read the data from a Json file on another page.
I have already written in the code where I want to get something where. I hope you know what I mean if not just ask under here. I always look in and answer. I've been through all sorts of sites but none of them helped me. So here now. You are my last hope
So hier is my Code
class PokemonDB extends StatefulWidget {
_PokemonDB createState() => _PokemonDB();
}
class _PokemonDB extends State<PokemonDB> {
List pokemon = const [];
Future loadPokemon() async {
var content = await rootBundle.loadString("json/pokemon.json");
var collection = json.decode(content);
setState(() {
pokemon = collection;
});
}
void initState() {
loadPokemon();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 0,
backgroundColor: Colors.black,
),
body: ListView.separated(
separatorBuilder: (BuildContext context, int index) => Divider(),
itemCount: pokemon.length,
itemBuilder: (BuildContext context, int index) {
var pokemonn = pokemon[index];
return Container(
child: ListTile(
onTap: () async{
Map name = await Navigator.push(
context,
MaterialPageRoute<Map>(builder: (BuildContext context) {
return ShinyHuntCounter(); <-- from here I want the data on which click was
},
),);
},
isThreeLine: false,
title: Text(
pokemonn['name'],
style: TextStyle(
fontFamily: 'pokemon',
fontSize: 30,
color: Colors.black
),
),
leading: Image(image: AssetImage(pokemonn['image'])),
),
decoration:
BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50)),
color: Color.fromRGBO(234, 180, 59, 1),
),
);
},
),
);
}
}
I think it would be simple like this,
In your ShinyHuntCounter class,
class ShinyHuntCounter extends StatefulWidget {
final String pokemonn;
const ShinyHuntCounter(this.pokemonn);
#override
ShinyHuntCounterState createState() => ShinyHuntCounterState();
}
class ShinyHuntCounterState extends State<ShinyHuntCounter> {
#override
Widget build(BuildContext context) {
return Text(widget.pokemonn); // Here you direct access using widget
}
}
and for passing the data, do something like this,
MaterialPageRoute<Map>(builder: (BuildContext context) {
return ShinyHuntCounter(pokemonn['name']); }, )
Hope that suits your case.