Could not launch Instance of 'Future<String>' - dart

I have the following FutureBuilder function in class A:
Future<String> GetYoutubeLink() async{
var link = "";
CollectionReference collectionRef =
Firestore.instance.collection("r");
Query query = collectionRef.where('name',
isEqualTo: name).limit(1);
QuerySnapshot collectionSnapshot = await query.getDocuments().then((data){
if(data.documents.length > 0){
link = data.documents[0].data['link'];
print(link);
}
});
return link.toString();
}
}
I am trying to set the link in class B as follows:
class _B extends State<B> {
String link = null;
void initState(){
super.initState();
setState(() {
A a = new A(widget.dish_name);
if(link == null) {
link = a.GetYoutubeLink().toString();
}
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(FontAwesomeIcons.youtubeSquare, size: 45,color:Colors.red),
onPressed: _launchURL,
),
],
);
}
_launchURL() async {
var url = link;
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
}
I am getting the following exception:
Could not launch Instance of 'Future'
Can someone tell me how to get the string instead of Future ?

Just Modify your code like this..
_launchURL() async{
CollectionReference collectionRef =
Firestore.instance.collection("r");
Query query = collectionRef.where('name',
isEqualTo: name).limit(1);
QuerySnapshot collectionSnapshot = await query.getDocuments().then((data){
if(data.documents.length > 0){
link = data.documents[0].data['link'];
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
});
}
And no need for initState() just remove that call.

Class a's method:
Future<String> getYoutubeLink() async {//its a good practice to set the method's name in lowerCamelCase
CollectionReference collectionRef = Firestore.instance.collection("r");
Query query = collectionRef.where('name', isEqualTo: name).limit(1);
QuerySnapshot collectionSnapshot = await query.getDocuments().then((data) {
try {
if (data.documents.length > 0) {
return data.documents[0].data['link'].toString(); //this will return the data as a string
}
} catch (e) {
return ""; //in case that something fails will return an empty string
}
});
return ""; //if it do not return anything will return an empty string
}
Class b:
class _B extends State<B> {
A a = new A(widget.dish_name);
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(FontAwesomeIcons.youtubeSquare, size: 45,color:Colors.red),
onPressed: _launchURL,
),
],
);
}
_launchURL() async {
var url = await a.getYoutubeLink();//call here your method. You are useing 'await' because this methods returns a Future, it means that the execution of this function should take some time
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
}
Do not call Future methods inside your initState() because it would crash your app. If you need to call Future methods that need time to be completed before building your widget you can use the FutureBuilder widget. Check here the documentation.
To know more about Future and async programing watch this video by MTechViral. I have learned a lot from him!

Related

Lost connection to device. Exited (sigterm) - Flutter Crash

My Flutter app is crashing on loading
It operates a FutureBuilder and I believe this to be where the issue comes from.
My app makes an API Call and returns the data to a map marker.
When i have the FutureBuilder return a list view it works fine.
However, when i change it to return a Stack containing my Map SDK and the buttons to call the API it crashes on start up.
Relevant code is below, thank you!
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Future<Stations> stations;
BuildContext _context;
MapMarkerExample _mapMarkerExample;
#override
void initState() {
stations = API_Call().fetchStations();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example 1'),
),
body: Container(
child: FutureBuilder<Stations>(
future: stations,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Error");
}
if (snapshot.connectionState == ConnectionState.done) {
return
Stack(
children: [
HereMap(onMapCreated: _onMapCreated),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
button('Stations Near Me', _anchoredMapMarkersButtonClicked),
button('Clear', _clearButtonClicked),
],
),
],
),
],
);
}
return Text("Loading");
}
)
)
);
}
api_call.dart
class API_Call {
Future<Stations> fetchStations() async {
var client = http.Client();
final response = await client.get(
'https://transit.hereapi.com/v8/stations?in=x,-x&return=transport&apiKey=API_KEY');
if (response.statusCode == 200) {
return Stations.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load stations');
}
}
}
api_manager.dart
typedef ShowDialogFunction = void Function(String title, String message);
class MapMarkerExample{
void showAnchoredMapMarkers() {
print('step5');
GeoCoordinates geoCoordinates = _callGeoCoordinates();
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
print('step6');
}
GeoCoordinates _callGeoCoordinates() {
print('step7');
var stations;
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
}
}
}
HereMapController _hereMapController;
List<MapMarker> _mapMarkerList = [];
MapImage _poiMapImage;
MapImage _circleMapImage;
ShowDialogFunction _showDialog;
List<MapMarker3D> _mapMarker3DList = [];
MapMarkerExample(ShowDialogFunction showDialogCallback, HereMapController hereMapController) {
_showDialog = showDialogCallback;
_hereMapController = hereMapController;
double distanceToEarthInMeters = 8000;
_hereMapController.camera.lookAtPointWithDistance(
GeoCoordinates(x, -x), distanceToEarthInMeters);
// Setting a tap handler to pick markers from map.
_setTapGestureHandler();
_showDialog("Note", "Tap markers for more.");
}
void clearMap() {
for (var mapMarker in _mapMarkerList) {
_hereMapController.mapScene.removeMapMarker(mapMarker);
}
_mapMarkerList.clear();
for (var mapMarker3D in _mapMarker3DList) {
_hereMapController.mapScene.removeMapMarker3d(mapMarker3D);
}
_mapMarker3DList.clear();
}
Future<void> _addPOIMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_poiMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/poi.png');
_poiMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
Anchor2D anchor2D = Anchor2D.withHorizontalAndVertical(0.5, 1);
MapMarker mapMarker = MapMarker.withAnchor(geoCoordinates, _poiMapImage, anchor2D);
mapMarker.drawOrder = drawOrder;
Metadata metadata = new Metadata();
metadata.setString("key_poi", "Next Departures");
mapMarker.metadata = metadata;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<void> _addCircleMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_circleMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/circle.png');
_circleMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
MapMarker mapMarker = MapMarker(geoCoordinates, _circleMapImage);
mapMarker.drawOrder = drawOrder;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<Uint8List> _loadFileAsUint8List(String assetPathToFile) async {
// The path refers to the assets directory as specified in pubspec.yaml.
ByteData fileData = await rootBundle.load(assetPathToFile);
return Uint8List.view(fileData.buffer);
}
void _setTapGestureHandler() {
_hereMapController.gestures.tapListener = TapListener.fromLambdas(lambda_onTap: (Point2D touchPoint) {
_pickMapMarker(touchPoint);
});
}
void _pickMapMarker(Point2D touchPoint) {
double radiusInPixel = 2;
_hereMapController.pickMapItems(touchPoint, radiusInPixel, (pickMapItemsResult) {
// Note that 3D map markers can't be picked yet. Only marker, polgon and polyline map items are pickable.
List<MapMarker> mapMarkerList = pickMapItemsResult.markers;
if (mapMarkerList.length == 0) {
print("No map markers found.");
return;
}
});
}
}
In api_manager.dart, this looks very suspicous, and you aren't returning anything from this function, it could also explain the error saying future not complete
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addPOIMapMarker(geoCoordinates, 1);
}
// GeoCoordinates geoCoordinates = stations.coordinates;
// _addPOIMapMarker(geoCoordinates, 1);
}
}
You have to return a Stations object from it, try after your for loop something like return stations;, it could fix your problem, if the error changes, it's also a good start.
Also change your line in future builder to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData)
And for the meantime, remove this _setTapGestureHandler(). The crash is most likely caused by some memory leak, and from the code posted, it could be explained by listeners.

ListView isnt updating state after I added a FutureBuilder

Before this problem happen I was dealing with Future handling to return the values I saved on a sharedPreference, however my _deleteTodo method was working just fine.
After I added the FutureBuilder and finally got my values rendered on the UI now I'm struggling with this new bug.
Every time I update the state of and item my UI reflects it but immediately undoes it
I tried to see if it was something with my _deleteTodo method so I changed the setState to only change the boolean from false to true, but it does exactly the same.
I also print the length of my List after the _deleteTodo and something funny happens: it works one time, the _deleteTodo erase the Todo but after that it doesn't work anymore
This is my TODO class
class Todo {
Todo ({this.title,this.isDone = false});
String title;
bool isDone;
//Decode method to convert a Json String into a Dynamic object
Todo.fromJson(Map <String, dynamic> json)
: title = json ["title"],
isDone = json ["isDone"];
Map <String,dynamic> toJson() =>
{
"title" : title,
"isDone" : isDone
};
}
This is my screen
class _TodoListScreenState extends State<TodoListScreen> {
List<Todo> todos = [];
//updates the state of the checkbox and reflects it on the UI
_toggleTodo(Todo todo, bool isChecked) {
setState(() {
todo.isDone = isChecked;
_deleteTodo(todo,isChecked);
print(todos.length);
});
}
_addTodo() async {
final todo = await showDialog<Todo>(
context: context,
builder: (BuildContext context) { // <- Here you draw the
Dialog
return NewTodoDialog();
},
);
if (todo != null) {
setState(() {
todos.add(todo);
_saveTodo(todos);
print(todos.length);
});
}
}
_deleteTodo (Todo todo, bool isDone) => (isDone)?
todos.remove(todo): debugPrint;
//Save you array object as an array of Strings in Shared Preferences
Future<void> _saveTodo(List<Todo> todo) async {
SharedPreferences sharedPreferences = await
SharedPreferences.getInstance();
sharedPreferences.setStringList("savedData", _mapTodoData(todo));
}
_mapTodoData(List<dynamic> todos) {
try {
var res = todos.map((v) => json.encode(v)).toList();
return res;
} catch (err) {
// Just in case
return ["Nope"];
}
}
Future<List> loadData() async {
SharedPreferences sharedPreferences = await
SharedPreferences.getInstance();
final List<Todo> todoArray =
_decodeTodoData(sharedPreferences.
getStringList("savedData")).toList();
todos = todoArray;
return todoArray;
}
List<Todo> _decodeTodoData(List<String> todos) {
try {
//Transforming List<String> to Json
var result = todos.map((v) => json.decode(v)).toList();
//Transforming the Json into Array<Todo>
var todObjects = result.map((v) => Todo.fromJson(v)).toList();
return todObjects;
} catch (error) {
return [];
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.deepPurple[900],
title: Text('Todo List')),
body: Container(
child: FutureBuilder(
future: loadData(),
builder: (BuildContext context, AsyncSnapshot snapshot){
return TodoList(
todos: todos,
onTodoToggle: _toggleTodo,
);
})
)
,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.purpleAccent[700],
child: Icon(Icons.add),
onPressed: _addTodo,
),
);
}
}
Thanks in advance, hope someone can help me :)
From what I can tell is that your _deleteTodo deletes from the local instance list todos but you are instructing flutter to rebuild the UI from disk via loadData which re-gets the data from disk.
My suggestion is to get the data once on page load using initState and from there only refer to the local instance of todos.
In your _deleteTodo you would also need to persist the state to disk or have a commit button somewhere
I Found the solution, inside my _toggleTodo i need it to save my TodoList after doing something to my todo.

Flutter: My list view is not updated when I modify an item

I am developing a 'todo' flutter app using BloC Architecture pattern.
My 'Home' ui displays todo list, and user can click the item's button to change the status from "todo" to "complete".
When an item is completed, it should display with another color distinct from other todos not completed.
But when I click the "complete" button, the list view is not updated.
Below is my UI code:
class HomePage extends StatelessWidget {
final TodoRepository _todoRepository;
final HomeBloc bloc;
HomePage(this._todoRepository) : this.bloc = HomeBloc(_todoRepository);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<List<Task>>(
stream: bloc.todos,
builder: (context, snapshot) {
return ListView(
children: snapshot.data.map(_buildItem).toList(),
);
}),
),
);
}
Widget _buildItem(Todo todo) {
if (todo.complete) {
return completed(todo);
} else {
return inCompleted(todo);
}
}
Widget inCompleted(Todo todo) {
return MaterialButton(
textColor: Colors.white,
color: Colors.green,
child: Text("Complete"),
onPressed: () {
bloc.done.add(todo);
}
);
}
Widget completed(Todo todo) {
return MaterialButton(
textColor: Colors.white,
color: Colors.red,
child: Text("Cancel"),
onPressed: () {
bloc.done.add(todo);
}
);
}
}
And here is my BloC class:
class HomeBloc {
final _getTodosSubject = PublishSubject<List<Todo>>();
final _doneTodoSubject = PublishSubject<Todo>();
final _cancelTodoSubject = PublishSubject<Todo>();
final TodoRepository _todoRepository;
var _todos = <Todo>[];
Stream<List<Todo>> get todos => _getTodosSubject.stream;
Sink<Todo> get done => _doneTodoSubject.sink;
Sink<Todo> get cancel => _doneTodoSubject.sink;
HomeBloc(this._todoRepository) {
_getTodos().then((_) {
_getTodosSubject.add(_todos);
});
_doneTodoSubject.listen(_doneTodo);
_cancelTodoSubject.listen(_cancelTodo);
}
Future<Null> _getTodos() async {
await _todoRepository.getAll().then((list) {
_todos = list;
});
}
void _doneTodo(Todo todo) {
todo.complete = true;
_update(todo);
}
void _cancelTodo(Todo todo) async {
todo.complete = false;
_update(todo);
}
void _update(Todo todo) async {
await _todoRepository.save(todo);
_getTodos();
}
}
It's because you don't "refresh" your list after calling getTodos() here's the modification:
HomeBloc(this._todoRepository) {
_getTodos() //Remove the adding part it's done in the function
_doneTodoSubject.listen(_doneTodo);
_cancelTodoSubject.listen(_cancelTodo);
}
Future<Null> _getTodos() async {
await _todoRepository.getAll().then((list) {
_todos = list;
_getTodosSubject.add(list); //You can actually remove the buffer _todos object
});
}
As I mention in the comment you can remove the _todos buffer but I don't want to refract to much you code.
With these few adjustents it's should work.
Hope it's help !!

How to continuously check internet connect or not on Flutter?

I use this code for check internet. and I wrap this function into initState also. Snack bar always displays when internet not available. But after connecting to the internet, the snack bar is not disappeared. I can't use connectivity plugin because they said on Android, the plugin does not guarantee connection to the Internet.
checking1(TextEditingController usernameController, BuildContext context,
String _url, GlobalKey<ScaffoldState> _scaffoldKey) async {
try {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
usernameController.text == '' ?
showDialog(...some code...) :
usernameValidation(usernameController.text, context, _url);
}
}
on SocketException
catch (_) {
_showSnackBar(_scaffoldKey);
}
}
Full example demonstrating a listener of the internet connectivity and its source.
Original post
import 'dart:async';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: HomePage()));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Map _source = {ConnectivityResult.none: false};
MyConnectivity _connectivity = MyConnectivity.instance;
#override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() => _source = source);
});
}
#override
Widget build(BuildContext context) {
String status = "Offline";
switch (_source.keys.toList()[0]) {
case ConnectivityResult.none:
status = "Offline";
break;
case ConnectivityResult.mobile:
status = "Mobile: Online";
break;
case ConnectivityResult.wifi:
status = "WiFi: Online";
break;
case ConnectivityResult.ethernet:
status = "Ethernet: Online";
break;
}
return Scaffold(
appBar: AppBar(title: Text("Internet")),
body: Center(child: Text(status)),
);
}
#override
void dispose() {
_connectivity.disposeStream();
super.dispose();
}
}
class MyConnectivity {
MyConnectivity._internal();
static final MyConnectivity _instance = MyConnectivity._internal();
static MyConnectivity get instance => _instance;
Connectivity connectivity = Connectivity();
StreamController controller = StreamController.broadcast();
Stream get myStream => controller.stream;
void initialise() async {
ConnectivityResult result = await connectivity.checkConnectivity();
_checkStatus(result);
connectivity.onConnectivityChanged.listen((result) {
_checkStatus(result);
});
}
void _checkStatus(ConnectivityResult result) async {
bool isOnline = false;
try {
final result = await InternetAddress.lookup('example.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isOnline = true;
} else {
isOnline = false;
}
} on SocketException catch (_) {
isOnline = false;
}
controller.sink.add({result: isOnline});
}
void disposeStream() => controller.close();
}
Another option also can be this package: https://pub.dartlang.org/packages/flutter_offline that deal with this issue really straightforward.
You need first to import the package 'package:flutter_offline/flutter_offline.dart';
After that you include the OfflineBuilder on Widget build(BuildContext context) and it will read all all stream changes from ConnectivityResult continuously.
Like the example on the link or like the following one
#override
Widget build(BuildContext context) {
return OfflineBuilder(
debounceDuration: Duration.zero,
connectivityBuilder: (
BuildContext context,
ConnectivityResult connectivity,
Widget child,
) {
if (connectivity == ConnectivityResult.none) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(child: Text('Please check your internet connection!')),
);
}
return child;
},
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text("Home")
),
body: new Column(
children: <Widget>[
new Container(
decoration: new BoxDecoration(color: Theme.of(context).cardColor),
child: _buildTxtSearchBox(),
),
new Divider(height: 10.0),
new FloatingActionButton.extended(
icon: Icon(Icons.camera_alt),
),
new Container(
...
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
drawer: MenuDrawer(),
)
);
}
The connectivity package will do what you want. It has an onConnectivityChanged stream which you can subscribe to. This will notify your app when the connectivity state changes. But just because your device is connected to a network doesn't mean it can access your server and be connected. So a DNS lookup would be a good idea before then updating the internal state of your application.
https://pub.dartlang.org/documentation/connectivity/latest/connectivity/Connectivity-class.html
I find this to be reliable & more convincing :
Future<bool> connectivityChecker() async {
var connected = false;
print("Checking internet...");
try {
final result = await InternetAddress.lookup('google.com');
final result2 = await InternetAddress.lookup('facebook.com');
final result3 = await InternetAddress.lookup('microsoft.com');
if ((result.isNotEmpty && result[0].rawAddress.isNotEmpty) ||
(result2.isNotEmpty && result2[0].rawAddress.isNotEmpty) ||
(result3.isNotEmpty && result3[0].rawAddress.isNotEmpty)) {
print('connected..');
connected = true;
} else {
print("not connected from else..");
connected = false;
}
} on SocketException catch (_) {
print('not connected...');
connected = false;
}
return connected;
}
Based on the bool value of connected returned, I'd run a timer based loop to check for internet again & again till its connected. Open to any suggestions

Selection of Item in DropdownButton causes Flutter to throw error

I am currently trying to retrieve data (tags) from a REST API and use the data to populate a dropdown menu which I can successfully do but upon selection of the item, I get the following error which according to this would mean that the "selected value is not member of the values list":
items == null || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
This occurs after the dropdown menu shows my selected item. However, this is error should not be occurring as I've done the necessary logging to check that the data is indeed assigned to the list in question. Could anyone help me resolve this issue? I have isolated it to down to it originating in the setState() method in onChanged of DropdownButton but can't seem to understand why that should be causing an issue. Any help would be deeply appreciated!
My code is as follows:
class _TodosByTagsHomePageState extends State<TodosByTagsHomePage> {
Tag selectedTag;
final Logger log = new Logger('TodosByTags');
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: ListView(
children: <Widget>[
FutureBuilder<List<Tag>> (
future: fetchTags(),
builder: (context, snapshot) {
if (snapshot.hasData) {
log.info("Tags are present");
_tagsList = snapshot.data;
return DropdownButton<Tag>(
value: selectedTag,
items: _tagsList.map((value) {
return new DropdownMenuItem<Tag>(
value: value,
child: Text(value.tagName),
);
}).toList(),
hint: Text("Select tag"),
onChanged: (Tag chosenTag) {
setState(() {
log.info("In set state");
selectedTag = chosenTag;
Scaffold.of(context).showSnackBar(new SnackBar(content: Text(selectedTag.tagName)));
});
},
) ;
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Container(width: 0.0, height: 0.0);
}),
])
);
}
// Async method to retrieve data from REST API
Future<List<Tag>> fetchTags() async {
final response =
await http.get(REST_API_URL);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
var result = compute(parseData, response.body);
return result;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
static List<Tag> parseData(String response) {
final parsed = json.decode(response);
return (parsed["data"] as List).map<Tag>((json) =>
new Tag.fromJson(json)).toList();
}
List<Tag> _tagsList = new List<Tag>();
}
// Model for Tag
class Tag {
final String tagName;
final String id;
final int v;
Tag({this.id, this.tagName, this.v});
factory Tag.fromJson(Map<String, dynamic> json) {
return new Tag(
id: json['_id'],
tagName: json['tagName'],
v: json['__v'],
);
}
}
update your code like this
I think issues that when calling setState in FutureBuilder that call fetchTags() move fetchTags() to initState() for once call
class _TodosByTagsHomePageState extends State<TodosByTagsHomePage> {
Tag selectedTag;
Future<List<Tag>> _tags;
#override
void initState() {
_tags = fetchTags();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: ListView(children: <Widget>[
FutureBuilder<List<Tag>>(
future: _tags,
builder: (context, snapshot) {
if (snapshot.hasData) {
return DropdownButton<Tag>(
value: selectedTag,
items: snapshot.data.map((value) {
print(value);
return DropdownMenuItem<Tag>(
value: value,
child: Text(value.tagName),
);
}).toList(),
hint: Text("Select tag"),
onChanged: (Tag chosenTag) {
setState(() {
selectedTag = chosenTag;
});
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return Container(width: 0.0, height: 0.0);
}),
]));
}

Resources