1
votes

I'm trying to build a drawer that gets it's items from json from a file. when I walk this through on debug, I can see it loads the items, but my final drawer results in an error. It states

[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: type 'List' is not a subtype of type 'Map<String, dynamic>' #0 loadDropDowns (package:carded1/main.dart:31:36)

To make this work,

  1. create an assets folder
  2. add this to pubspec.yaml:

assets:

  • assets/jsonfile.json
  1. add this to the jsonfile.json

    [{ "header": "Insurance", "listOfItems": ["henry", "jerry", "flurry"] }, { "header": "Insurance", "listOfItems": ["henry", "jerry", "flurry"] }, { "header": "Insurance", "listOfItems": ["henry", "jerry", "flurry"] } ]

Here is my main.dart file:

import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'dart:convert';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;

// String rawJson = '{"header":"Insurance","listOfItems":["henry","jerry","flurry"]}';
// Map<String, dynamic> map = jsonDecode(rawJson);
// String header = map['header'];
// List<dynamic> listOfItems = map['listOfItems'];

//List<ExpansionTile> listOfTilesToBuild = [];

void main() {

  runApp(FlipCarded());
}

Future wait(int seconds) {
  return new Future.delayed(Duration(seconds: seconds), () => {});
}

Future<String> _loadADropDownListAsset() async {
  return await rootBundle.loadString('assets/jsonfile.json');
}

Future<DropDownItem> loadDropDowns() async {
  await wait(5);
  String jsonString = await _loadADropDownListAsset();
  final jsonResponse = json.decode(jsonString);
  return new DropDownItem.fromJson(jsonResponse);
}

class FlipCarded extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlipCard',
      theme: ThemeData.dark(),
      home: HomePage(),
    );
  }
}


class DropDownItem{
  DropDownItem({this.header, this.listOfItems});

  final String header;
  final List<String> listOfItems;

  factory DropDownItem.fromJson(Map<String, dynamic> json){
    return DropDownItem(
      header: json['header'],
      listOfItems: List<String>.from(json['listOfItems'].map((x) => x)),
    );
  }


    Map<String, dynamic> toJson() {
      return {
        'header': header,
        'listOfItems': listOfItems,
      };
    }
}


class HomePage extends StatelessWidget {
  Widget futureDropdown(){
    return new FutureBuilder<DropDownItem>(
      future: loadDropDowns(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return new ExpansionListTile(header: snapshot.data.header, listOfItems: snapshot.data.listOfItems);
        } else if (snapshot.hasError) {
          return new ExpansionTile(title: Text('Error'));
        }
        return new CircularProgressIndicator();
      }
    );
  }

  //DropDownItem dItem = DropDownItem(header: header, listOfItems: listOfItems);

  _renderBg() {
    return Container(
      decoration: BoxDecoration(color: const Color(0xFFFFFFFF)),
    );
  }

  _renderAppBar(context) {
    return MediaQuery.removePadding(
      context: context,
      removeBottom: true,
      child: AppBar(
        brightness: Brightness.dark,
        elevation: 0.0,
        backgroundColor: Color(0xFFFFFFFF),
      ),
    );
  }

  _renderContext(context) {
    return Card(
        elevation: 0.0,
        margin:
            EdgeInsets.only(left: 32.0, right: 32.0, top: 20.0, bottom: 0.0),
        color: Color(0x00000000),
        child: FlipCard(
          direction: FlipDirection.VERTICAL,
          speed: 1000,
          onFlipDone: (status) {
            print(status);
          },
          front: Container(
            decoration: BoxDecoration(
              color: Color(0xFF006666),
              borderRadius: BorderRadius.all(Radius.circular(8.0)),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Back',
                  style: Theme.of(context).textTheme.headline1,
                ),
                Text('Click here to flip front',
                    style: Theme.of(context).textTheme.bodyText1),
              ],
            ),
          ),
          back: Container(
            decoration: BoxDecoration(
              color: Color(0xFF006666),
              borderRadius: BorderRadius.all(Radius.circular(8.0)),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Back',
                  style: Theme.of(context).textTheme.headline1,
                ),
                Text('Click here to flip back',
                    style: Theme.of(context).textTheme.bodyText1),
              ],
            ),
          ),
        ));
  }


  @override

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FlipCard'),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>[
            DrawerHeader(
              child: Text('Drawer'),
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
            ),
            futureDropdown(),
            //ExpansionListTile(header: dItem.header, listOfItems: dItem.listOfItems),
          ],
        ),
      ),
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          _renderBg(),
          Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              _renderAppBar(context),
              Expanded(
                flex: 4,
                child: _renderContext(context),
              ),
              Expanded(
                flex: 1,
                child: Container(),
              )
            ],
          )
        ],
      ),
    );
  }
}

class ExpansionListTile extends StatelessWidget {
  final String header;
  final List<String> listOfItems;

  ExpansionListTile({this.header, this.listOfItems});

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
        title: Text(header),
        children: List.generate(listOfItems.length, (index) {
          return GestureDetector(
            onTap: (){print('Yep i pressed shit');},
              child: Text(listOfItems[index].toString()));
        }));
  }
}
1

1 Answers

1
votes

When you do this:

Future<DropDownItem> loadDropDowns() async {
  await wait(5);
  String jsonString = await _loadADropDownListAsset();
  final jsonResponse = json.decode(jsonString);
  return new DropDownItem.fromJson(jsonResponse);
}

According to your JSON Data, jsonResponse will be a List<Map<String,dynamic>>.

But your factory DropDownItem.fromJson is waiting for Map<String, dynamic>` argument.


UPDATE - CODE RUNNING WITHOUT ERROR

Note: I didn't fully review the code.

import 'package:flutter/material.dart';
import 'package:flip_card/flip_card.dart';
import 'dart:convert';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;

// String rawJson = '{"header":"Insurance","listOfItems":["henry","jerry","flurry"]}';
// Map<String, dynamic> map = jsonDecode(rawJson);
// String header = map['header'];
// List<dynamic> listOfItems = map['listOfItems'];

//List<ExpansionTile> listOfTilesToBuild = [];

void main() {
  runApp(FlipCarded());
}

Future wait(int seconds) {
  return new Future.delayed(Duration(seconds: seconds), () => {});
}

Future<String> _loadADropDownListAsset() async {
  return await rootBundle.loadString('assets/jsonfile.json');
}

Future<List<DropDownItem>> loadDropDowns() async {
  // await wait(5);
  String jsonString = await _loadADropDownListAsset();
  final jsonResponse = json.decode(jsonString);
  return jsonResponse
      .map<DropDownItem>((json) => DropDownItem.fromJson(json))
      .toList();
}

class FlipCarded extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlipCard',
      theme: ThemeData.dark(),
      home: HomePage(),
    );
  }
}

class DropDownItem {
  DropDownItem({this.header, this.listOfItems});

  final String header;
  final List<String> listOfItems;

  factory DropDownItem.fromJson(Map<String, dynamic> json) {
    print('JSON: $json');
    return DropDownItem(
      header: json['header'],
      listOfItems: List<String>.from(json['listOfItems']),
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'header': header,
      'listOfItems': listOfItems,
    };
  }
}

class HomePage extends StatelessWidget {
  Widget futureDropdown() {
    return new FutureBuilder<List<DropDownItem>>(
        future: loadDropDowns(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView(
              shrinkWrap: true,
              children: snapshot.data
                  .map(
                    (dropDownItem) => ExpansionListTile(
                      header: dropDownItem.header,
                      listOfItems: dropDownItem.listOfItems,
                    ),
                  )
                  .toList(),
            );
          } else if (snapshot.hasError) {
            return new ExpansionTile(title: Text('Error'));
          }
          return new CircularProgressIndicator();
        });
  }

  //DropDownItem dItem = DropDownItem(header: header, listOfItems: listOfItems);

  _renderBg() {
    return Container(
      decoration: BoxDecoration(color: const Color(0xFFFFFFFF)),
    );
  }

  _renderAppBar(context) {
    return MediaQuery.removePadding(
      context: context,
      removeBottom: true,
      child: AppBar(
        brightness: Brightness.dark,
        elevation: 0.0,
        backgroundColor: Color(0xFFFFFFFF),
      ),
    );
  }

  _renderContext(context) {
    return Card(
        elevation: 0.0,
        margin:
            EdgeInsets.only(left: 32.0, right: 32.0, top: 20.0, bottom: 0.0),
        color: Color(0x00000000),
        child: FlipCard(
          direction: FlipDirection.VERTICAL,
          speed: 1000,
          onFlipDone: (status) {
            print(status);
          },
          front: Container(
            decoration: BoxDecoration(
              color: Color(0xFF006666),
              borderRadius: BorderRadius.all(Radius.circular(8.0)),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Back',
                  style: Theme.of(context).textTheme.headline1,
                ),
                Text('Click here to flip front',
                    style: Theme.of(context).textTheme.bodyText1),
              ],
            ),
          ),
          back: Container(
            decoration: BoxDecoration(
              color: Color(0xFF006666),
              borderRadius: BorderRadius.all(Radius.circular(8.0)),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Back',
                  style: Theme.of(context).textTheme.headline1,
                ),
                Text('Click here to flip back',
                    style: Theme.of(context).textTheme.bodyText1),
              ],
            ),
          ),
        ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FlipCard'),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>[
            DrawerHeader(
              child: Text('Drawer'),
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
            ),
            futureDropdown(),
            //ExpansionListTile(header: dItem.header, listOfItems: dItem.listOfItems),
          ],
        ),
      ),
      body: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          _renderBg(),
          Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              _renderAppBar(context),
              Expanded(
                flex: 4,
                child: _renderContext(context),
              ),
              Expanded(
                flex: 1,
                child: Container(),
              )
            ],
          )
        ],
      ),
    );
  }
}

class ExpansionListTile extends StatelessWidget {
  final String header;
  final List<String> listOfItems;

  ExpansionListTile({this.header, this.listOfItems});

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
        title: Text(header),
        children: List.generate(listOfItems.length, (index) {
          return GestureDetector(
              onTap: () {
                print('Yep i pressed shit');
              },
              child: Text(listOfItems[index].toString()));
        }));
  }
}