0
votes

I have an issue I don't understand.

   Widget build(BuildContext context) {
    List<Widget> menuList = menuCategories.map((category) {
      return ExpansionTile(
          title: CustomExpansionTileHeader(
            icon: category.icon,
            label: category.label,
          ),
          children: category.children
              .map((item) => ListTile(
                    title: Text(item.label),
                    onTap: item.onTap,
                  ))
              .toList());
    }).toList();
    print(menuList);
    menuList.insert(
        0,
        DrawerHeader(
            child: Text(
              '',
              style: TextStyle(color: Colors.white, fontSize: 25),
            ),
            decoration: BoxDecoration(
              color: Colors.green,
              // image: DecorationImage(
              // fit: BoxFit.fill,
              // image: AssetImage('assets/images/cover.jpg'))),
            )));
    return Drawer(
      child: ListView(padding: EdgeInsets.zero, children: menuList

As you can see I'm trying to create List type Widget withsome ExpansionTile widgets but also add DrawerHeader as index 0. Afterwards, I want to return the list as in Drawer widget in children List.

When I print menuList (before adding DrawerHeader to the list) it prints [ExpansionTile, ExpansionTile, ExpansionTile, ExpansionTile], when I print menuList.runtimeType I get List type ExpansionTile. And this is the part I don't understand. The list was supposed to be List of Widget and it was somehow changed.

This causes crash when I add DrawerHeader to the List as it's different tipe.

If it's important, I'm building Flutter web app.

Please help :)

2
Am I? According to the flutter docs definition of insert method: 'Inserts the object at position [index] in this list. This increases the length of the list by one and shifts all objects at or after the index towards the end of the list.'. As far as I undersand this method shoud just increase the lenght of the List. - artoniaz

2 Answers

3
votes

When you do List<Widget> menuList = menuCategories.map((category) {, specify the type with the generic field. Dart is incorrectly inferring that you want this iterable to be of type ExpansionTile.

Do

List<Widget> menuList = menuCategories.map<Widget>((category) {

to more explicitly tell dart what type you want this to be.

1
votes

The answer from @Christopher Moore is better than the approach mentioned below.

Its the tooList() call after mapping which always returns List<ExpansionTile>. This is more of a Dart't type safety mechanism. So instead of inserting to the existing list, collect it as separate list and use a spread operator and add those elements to a new list as shown below. Live version is available here in dartpad.

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Drawer Demo'),
        ),
        drawer: MyWidget(),
        body: Center(
          child: Text('Hello'),
        ),
      ),
    );
  }
}

class Item {
  String label;
  Function onTap;
  Item({this.label, this.onTap});
}

class Category {
  Icon icon;
  String label;
  List<Item> children;
  Category({this.icon, this.label, this.children});
}

class MyWidget extends StatelessWidget {
  final menuCategories = [
    Category(
      icon: Icon(Icons.ac_unit),
      label: 'ac_unit',
      children: [
        Item(label: 'Voltas'),
        Item(label: 'Haier'),
      ],
    ),
    Category(
      icon: Icon(Icons.computer),
      label: 'computers',
      children: [
        Item(label: 'HP'),
        Item(label: 'Dell'),
      ],
    ),
  ];
  @override
  Widget build(BuildContext context) {
    final categoryList = menuCategories.map((Category category) {
      return ExpansionTile(
          title: ListTile(
            title: Text(category.label),
          ),
          children: category.children
              .map((item) => ListTile(
                    title: Text(item.label),
                    onTap: item.onTap,
                  ))
              .toList());
    }).toList();

    List<Widget> menuList = [
      DrawerHeader(
        child: Text(
          'Categories',
          style: TextStyle(color: Colors.white, fontSize: 25),
        ),
        decoration: BoxDecoration(
          color: Colors.green,
          // image: DecorationImage(
          // fit: BoxFit.fill,
          // image: AssetImage('assets/images/cover.jpg'))),
        ),
      ),
      ...categoryList
    ];
    return Drawer(
        child: ListView(padding: EdgeInsets.zero, children: menuList));
  }
}

You can test the following piece of code in a normal dartpad to verify the same issue as you are seeing.

void main() {
  List<A> menus = [B('s'),B('fd')].toList();
  menus.insert(0, C('1'));
  print(menus);
}

class A {
  
}

class B extends A {
  String label;
  B(this.label);
}

class C extends A {
  String label;
  C(this.label);
}