Search code examples
flutterdio

DioError [DioErrorType.DEFAULT]: Converting object to an encodable object failed: Instance of 'FormData'


I'm new in Flutter. When i try to upload data to the server i faced some problems like: 1.NoSuchMethodError: The getter 'friendsList' was called on null 2.DioError [DioErrorType.DEFAULT]: Converting object to an encodable object failed: Instance of 'FormData'

import 'dart:convert';
import 'dart:io';

import 'package:data_collection/helperClass/testForAddButton.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http_parser/http_parser.dart';

class AutoCompleteDemo extends StatefulWidget {
  @override
  _AutoCompleteDemoState createState() => _AutoCompleteDemoState();
}

class _AutoCompleteDemoState extends State<AutoCompleteDemo> {
  final hospitalNameEng = TextEditingController();
  final _serviceKey = GlobalKey<FormState>();
  static List<String> friendsList = [];
  File imageFile;
  String servicejson;
  bool loading = true;

  @override
  void initState() {
    super.initState();
  }

  //for camera dialogBox
  Future<void> _showChoiceDialog(BuildContext context) {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return AlertDialog(
            title: Text("Make a Choice"),
            content: SingleChildScrollView(
              child: ListBody(
                children: <Widget>[
                  GestureDetector(
                    child: Text("Gallery"),
                    onTap: () {
                      _openGallery(context);
                    },
                  ),
                  Padding(padding: EdgeInsets.all(5.0)),
                  GestureDetector(
                    child: Text("Camera"),
                    onTap: () {
                      _openCamera(context);
                    },
                  )
                ],
              ),
            ),
          );
        });
  }

//for image
  Widget _decideImageView() {
    if (imageFile == null) {
      return Text("No Image Selected");
    } else {
      Image.file(
        imageFile,
        width: 400,
        height: 400,
      );
    }
    return Image.file(
      imageFile,
      width: 400,
      height: 400,
    );
  }

//Dio part
  Dio dio = new Dio();
  Future postData() async {
    final String apiUrl = "MY_API";
    setState(() {
      servicejson = jsonEncode(friendsList);
    });

    String imageFileName = imageFile.path.split('/').last;
    FormData formData = new FormData.fromMap({
      "image": await MultipartFile.fromFile(imageFile.path,
          filename: imageFileName, contentType: new MediaType('image', 'png')),
      "type": "image/png"
    });

    dynamic allOfTheUploadData = {
      "name": hospitalNameEng,
      "Services": servicejson,
      "Image": formData,
    };
    var response = await dio.post(apiUrl,
        data: allOfTheUploadData,
        options: Options(headers: {
          "accept": "*/*",
          "Authorization": "Bearer accresstoken",
          "Content-type": "multipart/form-data",
        }));

    return response.data;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            Container(
              //margin: const EdgeInsets.only(bottom:5.0),
              child: TextField(
                controller: hospitalNameEng,
                decoration:
                    InputDecoration(hintText: 'Hospital Name In English'),
              ),
              padding: EdgeInsets.all(10.0),
            ),
            //service
            Container(
              child: Form(
                key: _serviceKey,
                child: Padding(
                  padding: const EdgeInsets.all(5.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Services',
                        style: TextStyle(
                            fontWeight: FontWeight.w700, fontSize: 16),
                      ),
                      ..._getServices(),
                      SizedBox(
                        height: 20,
                      ),
                    ],
                  ),
                ),
              ),
            ),
            //camera
            Container(
              child: Center(
                child: Column(
                  children: <Widget>[
                    RaisedButton(
                      onPressed: () {
                        _showChoiceDialog(context);
                      },
                      child: Text("Select Image"),
                    ),
                    _decideImageView(),
                  ],
                ),
              ),
            ),
            //send to server
            Container(
              child: Center(
                child: Column(
                  children: <Widget>[
                    FlatButton(
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(18.0),
                          side: BorderSide(color: Colors.green)),
                      onPressed: () async {
                        try {
                          await postData().then((value) {
                            print(value);
                          });
                        } catch (e) {
                          print(e);
                        }
                      },
                      child: Text("Submit"),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  //services
  List<Widget> _getServices() {
    List<Widget> friendsTextFieldsList = [];
    for (int i = 0; i < friendsList.length; i++) {
      friendsTextFieldsList.add(Padding(
        padding: const EdgeInsets.symmetric(vertical: 16.0),
        child: Row(
          children: [
            Expanded(child: FriendTextFields(i)),
            SizedBox(
              width: 16,
            ),
            // we need add button at last friends row only
            _addRemoveButton(i == friendsList.length - 1, i),
          ],
        ),
      ));
    }
    return friendsTextFieldsList;
  }

  Widget _addRemoveButton(bool add, int index) {
    return InkWell(
      onTap: () {
        if (add) {
          // add new text-fields at the top of all friends textfields
          friendsList.insert(0, null);
        } else
          friendsList.removeAt(index);
        setState(() {});
      },
      child: Container(
        width: 30,
        height: 30,
        decoration: BoxDecoration(
          color: (add) ? Colors.green : Colors.red,
          borderRadius: BorderRadius.circular(20),
        ),
        child: Icon(
          (add) ? Icons.add : Icons.remove,
          color: Colors.white,
        ),
      ),
    );
  }

  //gallery
  _openGallery(BuildContext context) async {
    var picture = await ImagePicker.pickImage(source: ImageSource.gallery);
    setState(() {
      imageFile = picture;
    });
    Navigator.of(context).pop();
  }

  //Camera
  _openCamera(BuildContext context) async {
    var picture = await ImagePicker.pickImage(source: ImageSource.camera);
    setState(() {
      imageFile = picture;
    });
    Navigator.of(context).pop();
  }
}

For add button which i used in Widget _getservices() class

import 'package:autocomplete_textfield/autocomplete_textfield.dart';
import 'package:flutter/material.dart';

import '../players.dart';

class FriendTextFields extends StatefulWidget {
  final int index;
  FriendTextFields(this.index);
  final GlobalKey<_FriendTextFieldsState> serviceKey = new GlobalKey();
  @override
  _FriendTextFieldsState createState() => _FriendTextFieldsState();
}

class _FriendTextFieldsState extends State<FriendTextFields> {
  GlobalKey<AutoCompleteTextFieldState<Division>> key = new GlobalKey();
  TextEditingController _serviceController;
  AutoCompleteTextField searchTextField;

  void _loadData() async {
    await PlayersViewModel.loadPlayers();
  }

  @override
  void initState() {
    _loadData();
    super.initState();
    _serviceController = TextEditingController();
  }

  @override
  void dispose() {
    _serviceController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      // ignore: non_constant_identifier_names
      var _AutoCompleteDemoState;
      _serviceController.text = _AutoCompleteDemoState.friendsList[widget.index] ?? '';
    });
    var _AutoCompleteDemoState;
  
    return TextFormField(
      controller:
          _serviceController, // save text field data in friends list at index
      // whenever text field value changes

      onChanged: (v) => _AutoCompleteDemoState.friendsList[widget.index] = v,
      decoration: InputDecoration(hintText: 'Add a unique code: Service Name\''),
      validator: (v) {
        if (v.trim().isEmpty) return 'Please enter something';
        return null;
      },
    );
  }
}


Solution

    • Dio can't parse FormData instance if it is wrapped by another object or if you use nested FormData, so instead of doing this:
    FormData formData = new FormData.fromMap({
      "image": await MultipartFile.fromFile(imageFile.path,
          filename: imageFileName, contentType: new MediaType('image', 'png')),
      "type": "image/png"
    });
    
    dynamic allOfTheUploadData = {
      "name": hospitalNameEng,
      "Services": servicejson,
      "Image": formData,
    };
    

    do this:

    FormData formData = new FormData.fromMap({
      "name": hospitalNameEng,
      "Services": servicejson,
      "Image": {
          "image": await MultipartFile.fromFile(imageFile.path,
              filename: imageFileName, contentType: new MediaType('image', 'png')),
          "type": "image/png"
      },
    });
    

    or something like that but but you must not wrapped FormData with another object. Refer to this issue

    • Assuming that:
      1. You are new in flutter.
      2. You have only two dart files.
      3. Only one class has external dependacies.

    If the affirmations above are right, it is not necessary to use Provider, you can pass friendsList and the current index to the FriendTextFields: FriendTextFields(i, friendsList). And in the second file:

    class FriendTextFields extends StatefulWidget {
      final int index;
      final List<String> friendsList;
      FriendTextFields(this.index, this.friendsList);
      final GlobalKey<_FriendTextFieldsState> serviceKey = new GlobalKey();
      @override
      _FriendTextFieldsState createState() => _FriendTextFieldsState();
    }
    

    and

      @override
      Widget build(BuildContext context) {
        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
    
          _serviceController.text = widget.friendsList[widget.index] ?? '';
        });
        return TextFormField(
          controller:
              _serviceController, // save text field data in friends list at index
          // whenever text field value changes
    
          onChanged: (v) => widget.friendsList[widget.index] = v,
          decoration: InputDecoration(hintText: 'Add a unique code: Service Name\''),
          validator: (v) {
            if (v.trim().isEmpty) return 'Please enter something';
            return null;
          },
        );
      }