Home > Mobile >  The method .map is not defined for type FUTURE
The method .map is not defined for type FUTURE

Time:10-25

I am trying to pull a list of products from an REST api to show up in a dropdown. Here is my code:

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gateway_device/backend/api/api_services.dart';
import 'package:gateway_device/flutter_flow/flutter_flow_theme.dart';
import 'package:gateway_device/models/products_list_model.dart';
import 'package:gateway_device/routes/route_helper.dart';
import 'package:get/get.dart';

class EditDevicePortWidget extends StatefulWidget {
  final String deviceId;
  final String portId;
  final String publicId;
  final String label;

  const EditDevicePortWidget(
      {Key? key,
      required this.deviceId,
      required this.portId,
      required this.publicId,
      required this.label})
      : super(key: key);

  @override
  State<EditDevicePortWidget> createState() =>
      _EditDevicePortWidgetState(deviceId, portId, publicId, label);
}

class _EditDevicePortWidgetState extends State<EditDevicePortWidget> {
  final String deviceId;
  final String portId;
  final String publicId;
  final String label;
  late String dropDownValueSelected = '';
  Future<List<TankProduct>> dropDownProduct = ApiService().getAllProducts();
  late TextEditingController textController1 = TextEditingController();
  late TextEditingController textController2 = TextEditingController();
  late TextEditingController textController3 = TextEditingController();
  late TextEditingController textController4 = TextEditingController();

  @override
  void initState() {
    super.initState();
    String dropDownValueSelected = '';
    textController1.text = '';
    textController2.text = '';
    textController3.text = '';
    textController4.text = '';
  }

  _EditDevicePortWidgetState(
      this.deviceId, this.portId, this.publicId, this.label);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text(
          publicId   ' - '   label,
          style: FlutterFlowTheme.of(context).bodyText1.override(
                fontFamily: 'Heebo',
                fontSize: 18,
                fontWeight: FontWeight.w500,
              ),
        ),
        centerTitle: true,
        elevation: 0,
        backgroundColor: Colors.white,
        automaticallyImplyLeading: false,
        actions: [
          IconButton(
            hoverColor: Colors.transparent,
            // borderRadius: 30,
            // borderWidth: 1,
            iconSize: 40,
            icon: Icon(
              Icons.close,
              color: Colors.black,
              size: 30,
            ),
            onPressed: () {
              Get.offNamed(RouteHelper.getPortProfile(
                  deviceId, portId, publicId, label));
            },
          ),
        ],
        iconTheme: IconThemeData(color: Colors.black),
      ),
      body: SafeArea(
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height.h * 1,
          decoration: BoxDecoration(color: Colors.white),
          child: Column(
            children: [
              Row(
                        mainAxisSize: MainAxisSize.max,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: [
                          Padding(
                            padding:
                                EdgeInsetsDirectional.fromSTEB(0, 10, 0, 0),
                            child: Text(
                              'Product:',
                              style: FlutterFlowTheme.of(context)
                                  .bodyText1
                                  .override(
                                    fontFamily: 'Heebo',
                                    fontSize: 18,
                                    fontWeight: FontWeight.w500,
                                  ),
                            ),
                          ),
                        ],
                      ),
                      Row(
                        mainAxisSize: MainAxisSize.max,
                        children: [
                          DropdownButton<String>(
                              value: dropDownValueSelected,
                              items: dropDownProduct.map((item) => DropdownMenuItem<String>(
                                        value: item,
                                        child: Text(item,
                                            style: TextStyle(fontSize: 14)),
                                      ))
                                  .toList(),
                              onChanged: (item) => setState(() {
                                    dropDownValueSelected = item!;
                                  }))
                        ],
                      ),
            ],
          ),
        ),
      ),
    );
  }
}

The error I am getting is:

The method 'map' isn't defined for the type 'Future'. Try correcting the name to the name of an existing method, or defining a method named 'map'.

The error is from line 121.

Thanks in advance!

CodePudding user response:

ApiService().getAllProducts() returns a Future so you need to add await before ApiService().getAllProducts() to get List<TankProduct> that you can later use to show a dropdown.

List<TankProduct> dropDownProduct = [];

@override
  void initState() {
    super.initState();
    Future.delayed(Duration.zero, () {
     setState((){
         dropDownProduct = await ApiService().getAllProducts();
     });
    }
}

Now you can use the dropDownProduct list to show dropdown inside the DropdownButton widget.

DropdownButton<String>(
     value: dropDownValueSelected,
     items: dropDownProduct.map((item) {
              // ...
    })

CodePudding user response:

As VincentDR commented, dropDownProduct is a Future and it should be awaited. There are multiple ways to achieve this, some more better or prettier than others... I would suggest to remove this line: Future<List<TankProduct>> dropDownProduct = ApiService().getAllProducts(); Since you should not be invoking an async method and/or communicating with an API in your view. I suggest you investigate state handling and separating your app into layers.

But if you want a quick and functional solution, you can use a FutureBuilder to await for the future to complete, it should be something like this:

  FutureBuilder(
    future: ApiService().getAllProducts(),
    builder(BuildContext context, AsyncSnapshot<String> snapshot){
        // Validate if snapshot is loading or if there is an error
        if(snapshot.hasError){
          // Give feedback to the user about error
        } else if(!snapshot.hasData){
          // Give feedback to the user about loading in progress
        } else {
         return Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              DropdownButton<String>(
                  value: dropDownValueSelected,
                  items: dropDownProduct.map((item) => DropdownMenuItem<String>(
                            value: item,
                            child: Text(item,
                                style: TextStyle(fontSize: 14)),
                          ))
                      .toList(),
                  onChanged: (item) => setState(() {
                        dropDownValueSelected = item!;
                      }))
            ],
          );
        }
    }
  );
  • Related