I am trying to load a listview using flutter and dart but am having an issue, bare with me am new to flutter and learning by example https://github.com/didinj/flutter-crud-restapi-example/blob/master/lib/caseslist.dart am coming from a c# background. I obfuscated my api url to protect it it is valid my side.
class PlayerList extends StatelessWidget {
final List<Player> players;
PlayerList({Key key, this.players}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: players == null ? 0 : players.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.person),
title: Text(players[index].firstName),
subtitle: Text(players[index].surname.toString()),
),
));
});
}
}
The issue surrounds this line.
PlayerList({Key key, this.players}) : super(key: key);
It says key does not exist.
I am loading the list view as such?.
#override
Widget build(BuildContext context) {
if (players == null) {
players = api.getAllPlayers() as List<Player>;
}
return Scaffold(
appBar: AppBar(
title: const Text("Flutter ListView"),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: new Center(
child: new FutureBuilder(
future: loadList(),
builder: (context, snapshot) {
return players.length > 0
? new PlayerList(players: players)
: new Center(
child: new Text('No data found, tap plus button to add!',
style: Theme.of(context).textTheme.titleLarge));
},
)),
));
}
Future loadList() {
Future<List<Player>> playersApi = api.getAllPlayers();
playersApi.then((PlayerList) {
setState(() {
this.players = PlayerList;
});
});
return playersApi;
}
}
My Api Call is
class ApiService {
final String apiUrl = "https://secreturl/api";
final String getAllPlayersEndPoint = "/GetAllPlayers/";
Future<List<Player>> getAllPlayers() async {
final getallPlayersUrl = Uri.parse(apiUrl + getAllPlayersEndPoint);
Response res = await get(getallPlayersUrl);
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<Player> players =
body.map((dynamic item) => Player.fromJson(item)).toList();
return players;
} else {
throw "Failed to load cases list";
}
}
}
This is my Model
class Player {
final int id;
final int type;
final String playerLevel;
final String firstName;
final String surname;
Player(this.id, this.type, this.playerLevel, this.firstName, this.surname);
factory Player.fromJson(Map<String, dynamic> json) {
return Player(
json['id'],
json['type'],
json['playerlevel'],
json['firstname'],
json['surname'],
);
}
#override
String toString() =>
'Players{id: $id, firstName: $firstName, lastName: $surname}';
}
Related
I have a Flutter page that makes use of 2 data sources: one from API (Internet) and one from Shared Preferences. The API source has no problem, as I used FutureBuilder in the build() method. For the Shared Preferences, I have no idea how to apply another Future Builder (or should I add one more?). Here are the codes (I tried to simplify them):
Future<List<City>> fetchCities(http.Client client) async {
final response = await client
.get(Uri.parse('https://example.com/api/'));
return compute(parseCities, response.body);
}
List<City> parseCities(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<City>((json) => City.fromJson(json)).toList();
}
class CityScreen extends StatelessWidget {
static const routeName = '/city';
const CityScreen({super.key, required this.title});
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<City>>(
future: fetchCities(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.hasData) {
return CityList(cities: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
)
);
}
}
class CityList extends StatefulWidget {
const CityList({super.key, required this.cities});
final List<City> cities;
#override
State<CityList> createState() => _CityListState();
}
class _CityListState extends State<CityList> {
List<String> completedMissionIDs = [];
#override
void initState() {
super.initState();
Player.loadMissionStatus().then((List<String> result) {
setState(() {
completedMissionIDs = result;
if (kDebugMode) {
print(completedMissionIDs);
}
});
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: widget.cities.length * 2,
itemBuilder: (context, i) {
if (i.isOdd) return const Divider();
final index = i ~/ 2;
double completedPercent = _calculateCompletionPercent(widget.cities[index].missionIDs, completedMissionIDs);
return ListTile(
leading: const Icon(Glyphicon.geo, color: Colors.blue),
title: Text(widget.cities[index].cityName),
trailing: Text('$completedPercent%'),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => MissionScreen(title: '${widget.cities[index].cityName} Missions', cityId: widget.cities[index].id),
)
);
},
);
},
);
}
double _calculateCompletionPercent<T>(List<T> cityMissionList, List<T> completedList) {
if(cityMissionList.isEmpty) {
return 0;
}
int completedCount = 0;
for (var element in completedList) {
if(cityMissionList.contains(element)) {
completedCount++;
}
}
if (kDebugMode) {
print('Completed: $completedCount, Total: ${cityMissionList.length}');
}
return completedCount / cityMissionList.length;
}
}
The problem is, the build function in the _CityListState loads faster than the Player.loadMissionStatus() method in the initState, which loads a List<int> from shared preferences.
The shared preferences are loaded in the midway of the ListTiles are generated, making the result of completedPercent inaccurate. How can I ask the ListTile to be built after the completedPercent has been built?
Thanks.
I would start by making CityList a StatelessWidget that accepts completedMissionIDs as a constructor parameter.
Your CityScreen widget can call both APIs and combine the results into a single Future. Pass the combined Future to your FutureBuilder. That way you can render the CityList once all of the data has arrived from both APIs.
I put together a demo below:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
home: CityScreen(title: 'City Screen'),
));
}
class CombinedResult {
final List<City> cities;
final List<int> status;
const CombinedResult({
required this.cities,
required this.status,
});
}
class City {
final String cityName;
final List<int> missionIDs;
const City(this.cityName, this.missionIDs);
}
class Player {
static Future<List<int>> loadMissionStatus() async {
await Future.delayed(const Duration(seconds: 1));
return [0, 3];
}
}
Future<List<City>> fetchCities() async {
await Future.delayed(const Duration(seconds: 2));
return const [
City('Chicago', [1, 2, 3, 4]),
City('Helsinki', [1, 2, 3, 4]),
City('Kathmandu', [0, 4]),
City('Seoul', [1, 2, 3]),
];
}
class CityScreen extends StatefulWidget {
const CityScreen({super.key, required this.title});
final String title;
#override
State<CityScreen> createState() => _CityScreenState();
}
class _CityScreenState extends State<CityScreen> {
late Future<CombinedResult> _future;
#override
void initState() {
super.initState();
_future = _fetchData();
}
Future<CombinedResult> _fetchData() async {
final cities = await fetchCities();
final status = await Player.loadMissionStatus();
return CombinedResult(cities: cities, status: status);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder<CombinedResult>(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.hasData) {
return CityList(
cities: snapshot.data!.cities,
completedMissionIDs: snapshot.data!.status,
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class CityList extends StatelessWidget {
const CityList({
super.key,
required this.cities,
required this.completedMissionIDs,
});
final List<City> cities;
final List<int> completedMissionIDs;
#override
Widget build(BuildContext context) {
return ListView.separated(
padding: const EdgeInsets.all(16.0),
itemCount: cities.length,
separatorBuilder: (context, i) => const Divider(),
itemBuilder: (context, i) => ListTile(
leading: const Icon(Icons.location_city, color: Colors.blue),
title: Text(cities[i].cityName),
trailing: Text(
'${_calculateCompletionPercent(cities[i].missionIDs, completedMissionIDs)}%'),
),
);
}
double _calculateCompletionPercent<T>(
List<T> cityMissionList, List<T> completedList) =>
completedList.where(cityMissionList.contains).length /
cityMissionList.length;
}
First of all I would separate the data layer from the presentation. Bloc would be one example.
To combine 2 Futures you could do something like
final multiApiResult = await Future.wait([
sharedPrefs.get(),
Player.loadMissionStatus()
])
Hello guys I face this problem when I Create a Search Delegate in a flutter
I try to call data from Firebase and add it in List Class but it shows me this error
List<dynamic> is not a subtype fo type List<Itemshop> of function result
Problem Here
List<ItemShop> ItemShopList = [];
CollectionReference ItemShopRef =
FirebaseFirestore.instance.collection('ItemShop');
List filterItemShop = [];
int counter = 0;
Future getData() async {
var responsce = await ItemShopRef.get();
responsce.docs.forEach((element) {
ItemShop itemShop = ItemShop(element["ItemCatgore"], element["ItemImage"],
element["ItemKG"], element['ItemName'], element["ItemPrice"]);
if (counter == 0) {
ItemShopList.add(itemShop);
}
});
print(ItemShopList);
return ItemShopList;
}
Class for ItemShop
class ItemShop {
final String ItemCatgore, ItemImage, ItemKG, ItemName;
int ItemPrice;
ItemShop(this.ItemCatgore, this.ItemImage, this.ItemKG, this.ItemName,
this.ItemPrice);
}
Full Code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
#override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 184, 132, 132),
actions: [
IconButton(
onPressed: () {
showSearch(context: context, delegate: mySearch());
},
icon: Icon(Icons.search))
],
),
);
}
}
class mySearch extends SearchDelegate {
List<ItemShop> ItemShopList = [];
CollectionReference ItemShopRef =
FirebaseFirestore.instance.collection('ItemShop');
List filterItemShop = [];
int counter = 0;
Future getData() async {
var responsce = await ItemShopRef.get();
responsce.docs.forEach((element) {
ItemShop itemShop = ItemShop(element["ItemCatgore"], element["ItemImage"],
element["ItemKG"], element['ItemName'], element["ItemPrice"]);
if (counter == 0) {
ItemShopList.add(itemShop);
}
});
print(ItemShopList);
return ItemShopList;
}
////////////////////////////////////////////////
#override
List<Widget>? buildActions(BuildContext context) {
return [
IconButton(
onPressed: () {
query = "";
},
icon: Icon(Icons.close))
];
}
#override
Widget? buildLeading(BuildContext context) {
return IconButton(
onPressed: () {
close(context, null);
},
icon: Icon(Icons.arrow_back));
}
#override
Widget buildResults(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int i) {
return snapshot.data[i].ItemName == query
? Card(
child: Column(
children: [
Container(
color: Colors.grey[200],
height: 150,
width: double.infinity,
child: Text(
snapshot.data[i].ItemName,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35),
),
),
Container(
child: Text(snapshot.data[i].ItemName),
)
],
),
)
: Container();
});
}
});
}
#override
Widget buildSuggestions(BuildContext context) {
filterItemShop = ItemShopList.where((element) =>
element.ItemName.toLowerCase().contains(query.toLowerCase())).toList();
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount:
query == "" ? snapshot.data.length : filterItemShop.length,
itemBuilder: (BuildContext context, int i) {
return InkWell(
onTap: () {
query = query == ""
? ItemShopList[i].ItemName
: filterItemShop[i].ItemName;
showResults(context);
},
child: Card(
child: query == ""
? ListTile(
leading: Text(snapshot.data[i].ItemName),
title: Text(snapshot.data[i].ItemName),
subtitle: Text(snapshot.data[i].ItemName),
)
: ListTile(
leading: Text(filterItemShop[i].ItemName),
title: Text(filterItemShop[i].ItemName),
subtitle: Text(filterItemShop[i].ItemName),
),
),
);
});
}
});
}
}
class ItemShop {
final String ItemCatgore, ItemImage, ItemKG, ItemName;
int ItemPrice;
ItemShop(this.ItemCatgore, this.ItemImage, this.ItemKG, this.ItemName,
this.ItemPrice);
}
there, I think the better approach is not for each but map. and use type defines variable so you will not get type error as long as you do not use casting method. final List<Itemshop> x = responsce.docs.map((e)=>Itemshop.fromMap(e.data()..docId = e.id)).toList(); return x;
ypu can also just retun the map fucntion like return responsce.docs.map((e)=> ....
Itemshop should be ItemShop, standard dart format.
Itemshop.fromMap is a function that you build in Itemshop class. data classes always have this kind of helper. fromMap, toMap, fromJson, toJson. a lot of code generation in the dart for this if you don't want to write it yourself.
For example for your comment,
import 'dart:convert';
class ItemShop {
final String? itemCatgore;
final String? itemImage;
final String? ItemKG;
final String? ItemName;
final String? ItemPrice;
ItemShop({
this.itemCatgore,
this.itemImage,
this.ItemKG,
this.ItemName,
this.ItemPrice,
});
Map<String, dynamic> toMap() {
return {
'itemCatgore': itemCatgore,
'itemImage': itemImage,
'ItemKG': ItemKG,
'ItemName': ItemName,
'ItemPrice': ItemPrice,
};
}
factory ItemShop.fromMap(Map<String, dynamic> map) {
return ItemShop(
itemCatgore: map['itemCatgore'],
itemImage: map['itemImage'],
ItemKG: map['ItemKG'],
ItemName: map['ItemName'],
ItemPrice: map['ItemPrice'],
);
}
String toJson() => json.encode(toMap());
factory ItemShop.fromJson(String source) =>
ItemShop.fromMap(json.decode(source));
ItemShop copyWith({
String? itemCatgore,
String? itemImage,
String? ItemKG,
String? ItemName,
String? ItemPrice,
}) {
return ItemShop(
itemCatgore: itemCatgore ?? this.itemCatgore,
itemImage: itemImage ?? this.itemImage,
ItemKG: ItemKG ?? this.ItemKG,
ItemName: ItemName ?? this.ItemName,
ItemPrice: ItemPrice ?? this.ItemPrice,
);
}
#override
String toString() {
return 'ItemShop(itemCatgore: $itemCatgore, itemImage: $itemImage, ItemKG: $ItemKG, ItemName: $ItemName, ItemPrice: $ItemPrice)';
}
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ItemShop &&
other.itemCatgore == itemCatgore &&
other.itemImage == itemImage &&
other.ItemKG == ItemKG &&
other.ItemName == ItemName &&
other.ItemPrice == ItemPrice;
}
#override
int get hashCode {
return itemCatgore.hashCode ^
itemImage.hashCode ^
ItemKG.hashCode ^
ItemName.hashCode ^
ItemPrice.hashCode;
}
}
plus dart use camel case for all the variable and function (first latest is small later, second-word first letter is capital)
and all words first capital letter
Specify the type of future in futurebuilder, here it is list itemshop as shown below.
return FutureBuilder<List<ItemShop>>(
//TODO: YOUR CODE
);
I am using mobx and have split the code into 2 indvidualdata and indvidualdata provider and there is a autogenerated code with mobx.
The Listview.builder does not load the data until I hot reload the code (VSCode).
class IndividualDataState extends State<IndividualData> {
#override
void initState() {
super.initState();
setup();
sl<IIndividualDataProvider>()?.initReporting(context);
}
#override
Widget build(BuildContext context) {
return Observer(
builder: (_) => Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
elevation: 0,
centerTitle: true,
title: Text("All Data"),
backgroundColor: PRIMARY,
),
body: Stack(
children: <Widget>[
ListView.builder(
itemCount: sl<IIndividualDataProvider>().entries.length == null
? 0
: sl<IIndividualDataProvider>().entries.length,
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: new Text(sl<IIndividualDataProvider>()
.entries[index]
.entry
.toString()),
subtitle: new Text(sl<IIndividualDataProvider>()
.entries[index]
.createdAt
.toString()),
);
},
),
],
),
),
);
}
}
The provider
class IndividualDataProvider = IIndividualDataProvider
with _$IndividualDataProvider;
abstract class IIndividualDataProvider with Store {
#observable
bool isLoading = false;
#observable
List tags = [];
#observable
List<IndvidualReadings> entries = [];
#action
Future initReporting(context) async {
try {
isLoading = true;
Response _readings = await sl<IIndividualDataService>().getAllRmssd();
Map<String, dynamic> map = _readings.data;
List<dynamic> data = map["readings"];
if (data != null) {
data.forEach((v) {
IndvidualReadings tempRead = IndvidualReadings.fromJson(v);
entries.add(tempRead);
});
}
isLoading = false;
} catch (err) {
isLoading = false;
print(err.toString());
}
}
}
class IndvidualReadings {
double entry;
String createdAt;
List<ReadingTags> tags = [];
IndvidualReadings({this.entry, this.createdAt, this.tags});
factory IndvidualReadings.fromJson(Map<String, dynamic> json) {
var list = json['tags'] as List;
print(list.runtimeType);
List<ReadingTags> tagsList =
list.map((i) => ReadingTags.fromJson(i)).toList();
return IndvidualReadings(
entry: json['entry'], createdAt: json['created_at'], tags: tagsList);
}
}
class ReadingTags {
int id;
String tagName;
ReadingTags({this.id, this.tagName});
ReadingTags.fromJson(Map<String, dynamic> json) {
id = json['id'];
tagName = json['tag_name'];
}
}
When I click to open the page, it is blank. I had a few prints to see if the data is being pulled by the API and it was printing successfully.
Then when I just hot reload (I usually press Ctrl+S) the information is loaded correctly and the ListTile is rendered.
I am completely lost for words why this happens. Any help is appreciated.
You entries should be an ObservableList - then only the Observer widget will rebuild the changes in entries list automatically.
...
#observable
ObservableList<IndvidualReadings> entries = ObservableList;
...
I tried to migrate the no null safety code to null safety and I ended up with errors. I want to get autocomplete location of places in Flutter and display details on the tapped place.
Screenshots of errors:
The code:
main.dart
import 'package:flutter/material.dart';
import 'package:google_places_flutter/address_search.dart';
import 'package:google_places_flutter/place_service.dart';
import 'package:uuid/uuid.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Google Places Demo',
home: MyHomePage(title: 'Places Autocomplete Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _controller = TextEditingController();
String? _streetNumber = '';
String? _street = '';
String? _city = '';
String? _zipCode = '';
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
body: Container(
margin: EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
readOnly: true,
onTap: () async {
// generate a new token here
final sessionToken = Uuid().v4();
final Suggestion? result = await showSearch(
context: context,
delegate:AddressSearch(sessionToken),
);
// This will change the text displayed in the TextField
if (result != null) {
final placeDetails = await PlaceApiProvider(sessionToken)
.getPlaceDetailFromId(result.placeId);
setState(() {
_controller.text = result.description!;
_streetNumber = placeDetails.streetNumber;
_street = placeDetails.street;
_city = placeDetails.city;
_zipCode = placeDetails.zipCode;
});
}
},
decoration: InputDecoration(
icon: Container(
width: 10,
height: 10,
child: Icon(
Icons.home,
color: Colors.black,
),
),
hintText: "Enter address",
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: 8.0, top: 16.0),
),
),
SizedBox(height: 20.0),
Text('Street Number: $_streetNumber'),
Text('Street: $_street'),
Text('City: $_city'),
Text('ZIP Code: $_zipCode'),
],
),
),
);
}
}
address_search.dart
import 'package:flutter/material.dart';
import 'package:google_places_flutter/place_service.dart';
class AddressSearch extends SearchDelegate<Suggestion?> {
AddressSearch(this.sessionToken) {
apiClient = PlaceApiProvider(sessionToken);
}
final sessionToken;
late PlaceApiProvider apiClient;
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
tooltip: 'Clear',
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
)
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
tooltip: 'Back',
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
return Container();
}
#override
Widget buildSuggestions(BuildContext context) {
return FutureBuilder(
future: query == ""
? null
: apiClient.fetchSuggestions(
query, Localizations.localeOf(context).languageCode),
builder: (context, snapshot) => query == ''
? Container(
padding: EdgeInsets.all(16.0),
child: Text('Enter address'),
)
: snapshot.hasData
? ListView.builder(
itemBuilder: (context, index) =>
ListTile(
title:
Text((snapshot.data[index] as Suggestion).description!),
onTap: () {
close(context, snapshot.data[index] as Suggestion?);
},
),
itemCount: snapshot.data.length,
)
: Container(child: Text('Loading...')),
);
}
}
place_service.dart
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart';
class Place {
String? streetNumber;
String? street;
String? city;
String? zipCode;
Place({
this.streetNumber,
this.street,
this.city,
this.zipCode,
});
#override
String toString() {
return 'Place(streetNumber: $streetNumber, street: $street, city: $city, zipCode: $zipCode)';
}
}
class Suggestion {
final String? placeId;
final String? description;
Suggestion(this.placeId, this.description);
#override
String toString() {
return 'Suggestion(description: $description, placeId: $placeId)';
}
}
class PlaceApiProvider {
final client = Client();
PlaceApiProvider(this.sessionToken);
final sessionToken;
static final String androidKey = 'YOUR_API_KEY_HERE';
static final String iosKey = 'YOUR_API_KEY_HERE';
final apiKey = Platform.isAndroid ? androidKey : iosKey;
Future<List<Suggestion>?> fetchSuggestions(String input, String lang) async {
final request =
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&key=$apiKey&sessiontoken=$sessionToken';
final response = await client.get(Uri.parse(request));
if (response.statusCode == 200) {
final result = json.decode(response.body);
if (result['status'] == 'OK') {
// compose suggestions in a list
return result['predictions']
.map<Suggestion>((p) => Suggestion(p['place_id'], p['description']))
.toList();
}
if (result['status'] == 'ZERO_RESULTS') {
return [];
}
throw Exception(result['error_message']);
} else {
throw Exception('Failed to fetch suggestion');
}
}
Future<Place> getPlaceDetailFromId(String? placeId) async {
final request =
'https://maps.googleapis.com/maps/api/place/details/json?place_id=$placeId&fields=address_component&key=$apiKey&sessiontoken=$sessionToken';
final response = await client.get(Uri.parse(request));
if (response.statusCode == 200) {
final result = json.decode(response.body);
if (result['status'] == 'OK') {
final components =
result['result']['address_components'] as List<dynamic>;
// build result
final place = Place();
components.forEach((c) {
final List type = c['types'];
if (type.contains('street_number')) {
place.streetNumber = c['long_name'];
}
if (type.contains('route')) {
place.street = c['long_name'];
}
if (type.contains('locality')) {
place.city = c['long_name'];
}
if (type.contains('postal_code')) {
place.zipCode = c['long_name'];
}
});
return place;
}
throw Exception(result['error_message']);
} else {
throw Exception('Failed to fetch suggestion');
}
}
}
The solution is probably this:
builder: (context, AsyncSnapshot<List<Suggestion>> snapshot)
instead of this:
builder: (context, snapshot)
then you can do something like:
List<Suggestion>? suggestions = snapshot.data;
if ( suggestions != null && suggestions.length > 0) {
I'm new in flutter, I'd like to know how to add an item list dynamically to ListView without reloading data in FutureBuilder.
When I add an item to the ListView, it duplicate the list and then added the item to that list.
The Following code, include Model clas called Job.
JobListView is a stateful widget that include the dynamic ListView.
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
class Job {
#required
String company;
String description;
String employmentType;
int id;
String location;
String position;
List<String> skillsRequired;
Job(
this.company,
this.description,
this.employmentType,
this.id,
this.location,
this.position,
this.skillsRequired);
Job.fromJson(Map<String, dynamic> json) {
company = json['company'];
description = json['description'];
employmentType = json['employmentType'];
id = json['id'];
location = json['location'];
position = json['position'];
if (json['skillsRequired'] != null) {
skillsRequired = new List<String>();
json['skillsRequired'].forEach((v) {
skillsRequired.add(v);
});
}
}
}
class JobListView extends StatefulWidget {
#override
_JobListViewState createState() => _JobListViewState();
}
class _JobListViewState extends State<JobListView> {
List<Job> data = List<Job>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Job>>(
future: _getJob(),
builder: (context, snapshot) {
if (snapshot.hasData) {
data = snapshot.data;
return _listViewFormat(data);
} else if (snapshot.hasError) {
return Container();
}
return Center(
child: Container(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
},
) ,
floatingActionButton: (FloatingActionButton(child: Icon(Icons.add),onPressed: (){
setState(() {
var j = Job("CompanyX","Eng.5 position","Full-time",0,"Cairo","Senior",null);
data.add(j);
});
},)),
);
}
}
ListView _listViewFormat(List<Job> data) {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return _tile(data[index].position, data[index].description, Icons.work);
});
}
ListTile _tile(String title, String subtitle, IconData iconData) {
return ListTile(
title: Text(title, style: TextStyle(fontSize: 20)),
subtitle: Text(
subtitle,
style: TextStyle(fontSize: 12),
),
leading: Icon(iconData),
trailing: Icon(Icons.arrow_right),
);
}
Future<List<Job>> _getJob() async {
String baseUrl = 'https://mock-json-service.glitch.me';
var response = await get(baseUrl);
if (response.statusCode == 200) {
List jsonResponse = json.decode(response.body);
return jsonResponse.map((job) => new Job.fromJson(job)).toList();
}
}
Check out this more explanation How to deal with unwanted widget build?
if future changes you will see changes
Move _getJob method inside initState like this:
class _JobListViewState extends State<JobListView> {
List<Job> data = List<Job>();
Future<List<Job>> getJobFuture;
#override
void initState() {
super.initState();
getJobFuture = _getJob();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Job>>(
future: getJobFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
data = snapshot.data;
return _listViewFormat(data);
} else if (snapshot.hasError) {
return Container();
}
return Center(
child: Container(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
},
) ,
floatingActionButton: (FloatingActionButton(child: Icon(Icons.add),onPressed: (){
setState(() {
var j = Job("CompanyX","Eng.5 position","Full-time",0,"Cairo","Senior",null);
data.add(j);
});
},)),
);
}
}