flutterflutter-http

Can't get the futurebuilder data into the dropdownbutton to pick from there


I am trying to get some String data from a server via future builder, which works. Then transfer those strings into the dropdownbutton thing, to show as options then to be picked. I mean they will show up on dropdownbutton. Think of it like, I will choose a person to do a job here, I get the names from a database and show it on screen. So user can choose from there. Here is the important data that supposedly gets the dropdown data from the futurebuilder:

String dropdownValue = _MyAppState.data2.first;

It gives the following error:

Bad state: No element 

And here is my code:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

const List<String> list = <String>['One', 'Two', 'Three', 'Four'];
Future<List<Album>> fetchAlbum() async {
  final response = await http.get(
      Uri.parse('https://my-json-server.typicode.com/fluttirci/testJson/db'));
  //this was for testing.
  //print(response);
  Map<String, dynamic> userMap = jsonDecode(response.body);
  if (response.statusCode == 200) {
    return (userMap['employees'] as List)
        .map((e) => Album.fromJson(e))
        .toList();
  } else {
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  Album(this.userId, this.id, this.title);

  Album.fromJson(Map<String, dynamic> json)
      : userId = json['userId'],
        id = json['id'],
        title = json['title'];

  Map<String, dynamic> toJson() => {
        'userId': userId,
        'id': id,
        'title': title,
      };
}

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Future<Album> futureAlbum;
  late Future<List<Album>> user;
  late List<Album> data;
  static List<String> data2 = [];
  @override
  void initState() {
    super.initState();
    user = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        brightness: Brightness.dark,
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Column(children: <Widget>[
          const Expanded(
            child: DropdownButtonExample(),
          ),
          Expanded(
            child: FutureBuilder<List<Album>>(
              future: user,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  data = snapshot.data ?? [];

                  return ListView.builder(
                    itemBuilder: (context, index) {
                      data2.add(data[index].title);
                      print('data2 was fetched: ${data2[index]}');
                      return Column(
                        children: [
                          Text(data[index].title),
                        ],
                      );
                    },
                    itemCount: data.length,
                  );
                } else if (snapshot.hasError) {
                  return Text(
                    '${snapshot.error}',
                  );
                }
                return const Center(
                  child: CircularProgressIndicator(),
                );
              },
            ),
          )
        ]),
      ),
    );
  }
}

class DropdownButtonApp extends StatelessWidget {
  const DropdownButtonApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('DropdownButton Sample')),
        body: const Center(
          child: DropdownButtonExample(),
        ),
      ),
    );
  }
}

class DropdownButtonExample extends StatefulWidget {
  const DropdownButtonExample({super.key});

  @override
  State<DropdownButtonExample> createState() => _DropdownButtonExampleState();
}

class _DropdownButtonExampleState extends State<DropdownButtonExample> {
  String dropdownValue = _MyAppState.data2.first;

  @override
  Widget build(BuildContext context) {
    return DropdownButton<String>(
      value: dropdownValue,
      icon: const Icon(Icons.arrow_downward),
      elevation: 16,
      style: const TextStyle(color: Colors.deepPurple),
      underline: Container(
        height: 2,
        color: Colors.deepPurpleAccent,
      ),
      onChanged: (String? value) {
        // This is called when the user selects an item.
        setState(() {
          dropdownValue = value!;
        });
      },
      items: list.map<DropdownMenuItem<String>>((String value) {
        return DropdownMenuItem<String>(
          value: value,
          child: Text(value),
        );
      }).toList(),
    );
  }
}

Solution

  • There are more than one error in your code.

    Error 1 :

    You are trying to access first element from empty list. At String dropdownValue = _MyAppState.data2.first;

    Actual issue of Bad state: No element

    You can give only dropdown items as value. In your case the data coming from api and hardcoded list List<String> list = <String>['One', 'Two', 'Three', 'Four']; both are different.

    Hope this helps