0

我有这个问题,我有一个两步表格。我加载了一个本地 JSON 文件以启动状态,以便在下拉小部件的第二步中使用。但是,当我单击继续按钮并切换到第二种形式时,变量更改为 null 导致出现错误“方法 'map' 在 null 上被调用”。我尝试在 continueButton 小部件的 onPressed 函数中加载数据,并在创建第二种形式的小部件之一时尝试再次加载。但是我总是收到这个错误,即在 null 上调用了 map 函数。请帮忙 !!!

import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';
import 'package:ofepaw/services/auth.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;

class RegisterPage extends StatefulWidget {
  final Function toggleView;
  RegisterPage({this.toggleView});
  @override
  State createState() => RegisterPageState();
}

class RegisterPageState extends State<RegisterPage> {
  final AuthService _auth = AuthService();

  // store the step we are in
  int step = 1;

  // Text Field
  String emailOrPhone = "";
  String pass = "";
  String verifyPass = "";
  String firstName = "";
  String lastName = "";
  String department = "";
  String arrondissement = "";
  String commune = "";

  // variable for errors
  String error = "";

  // array containing the forms
  var form1;
  var form2;

  // Controllers for the Textfields
  final _emailOrPhoneController = TextEditingController();
  final _passwordController = TextEditingController();
  final _verifyPassController = TextEditingController();
  final _firstNameController = TextEditingController();
  final _lastNameController = TextEditingController();
  //final _departmentController = TextEditingController();

  //load the geographic division of Haiti json file
  var division;

  // get the locations data
  Future<void> loadDivision() async {
    String jsonString = await rootBundle
        .loadString('assets/haiti_administrative_district.json');
    Map<String, dynamic> map = json.decode(jsonString);
    List<dynamic> data = map["departments"];
    setState(() {
      division = data;
    });
  }

  // function to register user in ofepaw database
  Future registerUser(
      String id, String fname, String lname, String department) async {
    final String url = "url go here";

    final response = await http.post(url, body: {
      "id": id,
      "fname": fname,
      "lname": lname,
      "department": department
    });

    if (response.statusCode == 201) {
      print("This is the response from the server");
      print(response.body);
      print("Response passed");
    } else {
      print("Error occured");
    }

    print(" The response is : ");
    print(response.body.toString());
  }

  //functions for switching forms
  getForm(int form) {
    if (form == 1) {
      return form1;
    } else if (form == 2) {
      return form2;
    }
  }

  @override
  void initState() {
    super.initState();
    loadDivision();
    print(division);
    form1 = <Widget>[
      firstForm(),
      Text("Or Sign Up with social media"),
      SizedBox(
        height: 20,
      ),
      socialMediaButtons(),
      SizedBox(
        height: 50,
      ),
      InkWell(
          onTap: () {
            widget.toggleView();
          },
          child: Text("Have an account? Login"))
    ];
    form2 = <Widget>[
      secondForm(),
      InkWell(
          //back button
          onTap: () => setState(() => step = 1),
          child: Text("Back"))
    ];
  }

  // dispose all controllers
  // dispose of all TextEditingControllers
  @override
  void dispose() {
    _emailOrPhoneController.dispose();
    _passwordController.dispose();
    _verifyPassController.dispose();
    _firstNameController.dispose();
    _lastNameController.dispose();
    //_departmentController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Expanded(
        child: Stack(
          children: <Widget>[
            // banner with picture
            Positioned(
              child: banner(),
            ),
            // Login Elements Container
            Positioned(
              child: Container(
                margin: EdgeInsets.only(top: 300.0),
                decoration: BoxDecoration(
                    color: Colors.white,
                    boxShadow: [
                      BoxShadow(
                          color: Colors.grey.withOpacity(0.2),
                          spreadRadius: 5,
                          blurRadius: 20,
                          offset: Offset(0, 0))
                    ],
                    borderRadius: BorderRadius.only(
                        topRight: Radius.circular(50),
                        topLeft: Radius.circular(50))),
                child: Center(
                  child: Column(
                    children: getForm(step),
                  ),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }

  Widget banner() {
    return Container(
      height: 350,
      decoration: BoxDecoration(color: Colors.white),
      child: Center(
        child: Image.network(
            "https://image.freepik.com/free-vector/man-using-his-phone-instead-working_23-2148501890.jpg"),
      ),
    );
  }

  Widget emailField() {
    return TextFormField(
      controller: _emailOrPhoneController,
      decoration: InputDecoration(
          contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
          border: InputBorder.none,
          hintText: "Email or Phone Number",
          hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
      onChanged: (val) {
        setState(() => emailOrPhone = val);
      },
    );
  }

  Widget firstNameField() {
    return TextFormField(
      controller: _firstNameController,
      decoration: InputDecoration(
          contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
          border: InputBorder.none,
          hintText: "First Name",
          hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
      onChanged: (val) {
        setState(() => firstName = val);
      },
    );
  }

  Widget lastNameField() {
    return TextFormField(
      controller: _lastNameController,
      decoration: InputDecoration(
          contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
          border: InputBorder.none,
          hintText: "Last Name",
          hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
      onChanged: (val) {
        setState(() => lastName = val);
      },
    );
  }

  Widget departmentField() {
    return DropdownButtonFormField(
      items: division.map<DropdownMenuItem<String>>((value) {
        return DropdownMenuItem<String>(
          value: value["name"],
          child: Text(value["name"]),
        );
      }).toList(),
      hint: Text("Choose the department"),
      onChanged: (value) {
        setState(() {});
      },
      decoration: InputDecoration(
          contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
          border: InputBorder.none,
          hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
    );
  }

  Widget arrondissementField() {
    return DropdownButtonFormField(
        items: [
          DropdownMenuItem(
            child: Text("Delmas"),
            value: 1,
          ),
          DropdownMenuItem(
            child: Text("Petion-Ville"),
            value: 2,
          )
        ],
        hint: Text("Choose the department"),
        onChanged: (int value) {
          setState(() {});
        },
        decoration: InputDecoration(
            contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
            border: InputBorder.none,
            hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)));
  }

  Widget communeField() {
    return DropdownButtonFormField(
        items: [
          DropdownMenuItem(
            child: Text("Delmas"),
            value: 1,
          ),
          DropdownMenuItem(
            child: Text("Petion-Ville"),
            value: 2,
          )
        ],
        hint: Text("Choose the department"),
        onChanged: (int value) {
          setState(() {});
        },
        decoration: InputDecoration(
            contentPadding: EdgeInsets.only(top: 10, left: 20, right: 20),
            border: InputBorder.none,
            hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)));
  }

  Widget passwordField() {
    return TextFormField(
        controller: _passwordController,
        decoration: InputDecoration(
            contentPadding: EdgeInsets.only(bottom: 5, left: 20, right: 20),
            border: InputBorder.none,
            hintText: "Password",
            hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
        onChanged: (val) {
          setState(() => pass = val);
        });
  }

  Widget verifyPasswordField() {
    return TextFormField(
        controller: _verifyPassController,
        decoration: InputDecoration(
            contentPadding: EdgeInsets.only(bottom: 5, left: 20, right: 20),
            border: InputBorder.none,
            hintText: "Reenter Password",
            hintStyle: TextStyle(color: Colors.grey[400], fontSize: 15)),
        onChanged: (val) {
          setState(() => verifyPass = val);
        });
  }

  Widget socialMediaButtons() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        SignInButton(Buttons.Facebook,
            mini: true, onPressed: () => print("Facebook Sign in ...")),
        SignInButton(Buttons.Google,
            //mini: true,
            onPressed: () => print("Google Sign in ..."))
      ],
    );
  }

  Widget continueButton() {
    return ButtonTheme(
        minWidth: 185.0,
        height: 48.0,
        child: RaisedButton(
          color: Colors.black,
          textColor: Colors.white,
          child: Text("continue"),
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0)),
          onPressed: () {
            print(division);
            setState(() {
              step = 2;
            });
          },
        ));
  }

  Widget signUpButton() {
    return ButtonTheme(
        minWidth: 185.0,
        height: 48.0,
        child: RaisedButton(
          color: Colors.black,
          textColor: Colors.white,
          child: Text("Sign Up"),
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(50.0)),
          onPressed: () async {
            dynamic result =
                await _auth.registerWithEmailAndPassword(emailOrPhone, pass);

            if (result == null) {
              setState(() => error = "Email or Password incorrect");
            } else {
              // Register user ID in the server database
              registerUser(result.user.uid, firstName, lastName, department);
            }
          },
        ));
  }

  Widget emailPassField() {
    return Container(
      // Form input field
      margin: EdgeInsets.only(top: 40.0),
      width: 320.0,
      height: 180.0,
      decoration: BoxDecoration(
          color: Colors.white,
          boxShadow: [
            BoxShadow(
                color: Colors.grey.withOpacity(0.2),
                spreadRadius: 0.2,
                blurRadius: 5,
                offset: Offset(0, 0))
          ],
          borderRadius: BorderRadius.circular(20)),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          emailField(),
          Divider(),
          passwordField(),
          Divider(),
          verifyPasswordField()
        ],
      ),
    );
  }

  Widget personalInfoField() {
    return Container(
      // Form input field
      margin: EdgeInsets.only(top: 40.0),
      width: 320.0,
      height: 310.0,
      decoration: BoxDecoration(
          color: Colors.white,
          boxShadow: [
            BoxShadow(
                color: Colors.grey.withOpacity(0.2),
                spreadRadius: 0.2,
                blurRadius: 5,
                offset: Offset(0, 0))
          ],
          borderRadius: BorderRadius.circular(20)),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          firstNameField(),
          Divider(),
          lastNameField(),
          Divider(),
          departmentField(),
          Divider(),
          arrondissementField(),
          Divider(),
          communeField()
        ],
      ),
    );
  }

  Widget firstForm() {
    return Column(
      children: <Widget>[
        emailPassField(),
        SizedBox(
          height: 50,
        ),
        continueButton(),
        SizedBox(
          height: 50,
        ),
      ],
    );
  }

  Widget secondForm() {
    return Column(
      children: <Widget>[
        personalInfoField(),
        SizedBox(
          height: 40,
        ),
        signUpButton(),
        SizedBox(
          height: 40,
        ),
      ],
    );
  }
}
4

2 回答 2

0
Future<void> loadDivision() async {
    String jsonString = await rootBundle
        .loadString('assets/haiti_administrative_district.json');
    Map<String, dynamic> map = json.decode(jsonString);
    List<dynamic> data = map["departments"];
    setState(() {
      division = data;
    });
  }

在这里,首先确保数据被加载到 jsonString 中。
其次,确保json.decode(jsonString)返回 json 对象而不是 json 对象数组。

如果它返回 json 对象数组,请将其存储在List<Map>.
然后做一个这样的列表:

List<Map> maps = json.decode(jsonString);
  List data=new List();
  maps.forEach((element)=>data.add(element['departments']));
于 2021-04-03T07:10:01.777 回答
0

问题的根源从这里开始String jsonString = await rootBundle.loadString('assets/haiti_administrative_district.json');

您确定您的项目可以识别 thise:'assets/haiti_administrative_district.json'吗?您是否已将其添加到 pubspec.yaml 中的评估中?

因为这是行返回 null,所以向下的所有内容都将返回 null,包括您收到的错误。

于 2021-04-03T07:01:54.023 回答