一尘不染

如何在Flutter中仅解析一次JSON

flutter

我正在制作一个通过JSON解析获取值的应用。我的应用程序有多个标签,但是每次在标签之间滑动时,JSON每次都会发送一个新的读取请求。下面是我的代码:

Home.dart(按住导航选项卡)

import 'package:flutter/material.dart';
import './First.dart' as first;
import './Second.dart' as second;
import './Third.dart' as third;
import './Fourth.dart' as fourth;
import './Fifth.dart' as fifth;


class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => new _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  final List<NewPage> _tabs = [
    new NewPage(title: "Providers Near Me",color: Colors.blue[500]),
    new NewPage(title: "Providers Search",color: Colors.blueGrey[500]),
    new NewPage(title: "Providers List",color: Colors.teal[500]),
    new NewPage(title: "My Info",color: Colors.indigo[500]),
    new NewPage(title: "My Dependents Info",color: Colors.red[500]),
  ];
  NewPage _myHandler;
  TabController tabController;
  String pos = 'top';

  void initState(){
    super.initState();
    tabController = new TabController(length: 5, vsync: this);
    _myHandler = _tabs[0];
    tabController.addListener(_handleSelected);
  }

  void _handleSelected() {
    setState(() {
      _myHandler = _tabs[tabController.index];
    });
  }

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

  ///
  /// This method defines the different tabs in the Tab Bar. This is the
  /// constructor for the Navigation Bar that will be used by the user most.
  ///
  TabBar navbar (){
    return TabBar(
      controller: tabController,
      tabs: <Widget>[
        new Tab(
          icon: new Icon(Icons.healing),
        ),
        new Tab(
          icon: new Icon(Icons.search),
        ),
        new Tab(
          icon: new Icon(Icons.list),
        ),
        new Tab(
          icon: new Icon(Icons.person),
        ),
        new Tab(
          icon: new Icon(Icons.group),
        ),
      ],
    );
  }


  ///
  /// This method returns the App Bar properties. Its takes in an argument to
  /// determining if the Tab Bar should be at the top or at the bottom of the
  /// screen. If the Tab Bar is to be at the top of the screen, it will return
  /// the AppBar with the bottom property. If the Tab Bar is to be at the
  /// bottom, it will return the AppBar without the bottom property
  ///
  AppBar barController(String position){
    if (position == 'top'){
      return AppBar(
        title: new Text(_myHandler.title),
        backgroundColor: _myHandler.color,
        bottom: navbar(),
      );
    }
    else if (position == 'bottom'){
      return AppBar(
        title: new Text(_myHandler.title),
        backgroundColor: _myHandler.color,
      );
    }
    else{
      return null;
    }
  }


  ///
  /// This method controls the Navigation Bar at the bottom of the page. If the
  /// navigation bar is to be displayed at the bottom, then the navigation bar
  /// will be returned. Else, null will be returned.
  ///
   Material bottomBarController(String disp){
    if (disp == 'bottom'){
      return Material(
        color: _myHandler.color,
        child: navbar(),
      );
    }
    else{
      return null;
    }
  }

  @override
  Widget build(BuildContext context){
    return new Scaffold(
    endDrawer: new AppDrawer(),
      appBar: barController(pos),
      body: new TabBarView(
        children: <Widget>[
          new first.First(),
          new second.MapPage(),
          new third.Third(),
          new fourth.Fourth(),
          new fifth.Fifth(),
        ],
        controller: tabController,
      ),
      bottomNavigationBar: bottomBarController(pos)
    );
  }
}

// Appdrawer
// This method opens a drawer where more settings are available to control
// according to user needs.

class AppDrawer extends StatefulWidget {
  @override
  _AppDrawerState createState() => _AppDrawerState();
}

class _AppDrawerState extends State<AppDrawer> {

  bool _value = false;
  String message = "This is true";
  void onChanged(bool value){

    if(value){
      setState(() {
        message = "This is true";
        print(message.toString());
        String pos = "top";
        _value = true;
      });
    }else{
      setState(() {
        message = "This is false";
        print(message.toString());
        String pos = "bottom";
        _value = false;
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return Drawer(
      child: new ListView(
        children: <Widget>[
          new UserAccountsDrawerHeader(
            accountName: new Text("Suman Kumar"),
            accountEmail: new Text ("Shoeman360@gmail.com"),
          ),
          new ListTile(
            title: new Text("Settings"),
            trailing: new Icon(Icons.settings),
          ),
          new SwitchListTile(
              title: new Text("NavBar Position"),
              activeColor: Colors.indigo,
              value: _value,
              onChanged: (bool value){
                onChanged(value);
                new Text (message);
              }
          ),
          new ListTile(
            title: new Text("Close"),
            trailing: new Icon(Icons.cancel),
            onTap: () => Navigator.pop(context),
          ),
        ],
      ),
    );
  }
}

class NewPage {
  final String title;
  final Color color;
  NewPage({this.title,this.color});
}

Fourth.dart(调用JSON API的类之一)

import 'package:flutter/material.dart';
import 'package:emas_app/Dependant.dart' as Dep;
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'model/crm_single_user_model.dart';

final String url = "http://crm.emastpa.com.my/MemberInfo.json";

//Future class for single user information
Future<SingleUser> fetchUser() async{

  final response =  await http.get(url);
  final jsonresponse = json.decode(response.body);

  return SingleUser.fromJson(jsonresponse[0]["Employee"]);
}

Future<String> jsonContent() async {
  var res = await http.get(
      Uri.encodeFull(
          "http://crm.emastpa.com.my/MemberInfo.json"),
          headers: {"Accept": "application/json"});
  return res.body;
}

class Fourth extends StatefulWidget {

  @override
  FourthState createState() {
    return new FourthState();
  }
}

class FourthState extends State<Fourth> {
  //String name;

  @override
  Widget build(BuildContext context) {

    //New body widget
    Widget newbody = new Container(
      child: new Center(
        child: new FutureBuilder(
          future: fetchUser(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              var userdata = snapshot.data;

              //get the data from snapshot
              final name = userdata.name;
              final id = userdata.identification;
              final company = userdata.company;
              final dob = userdata.dob;

              return new Card(
                child: new Column(
                  children: <Widget>[
                    new ListTile(
                      title: new Text("Name"),
                      subtitle: new Text(name),
                    ),
                    new ListTile(
                      title: new Text("Identification"),
                      subtitle: new Text(id),
                    ),
                    new ListTile(
                      title: new Text("Company"),
                      subtitle: new Text(company),
                    ),
                    new ListTile(
                      title: new Text("Date of Birth"),
                      subtitle: new Text(dob),
                    ),
                    const Divider(
                          color: Colors.white,
                          height: 50.0,
                        ),
                        new MaterialButton(
                          color: Colors.indigo,
                          height: 50.0,
                          minWidth: 50.0,
                          textColor: Colors.white,
                          child: new Text("More"),
                          onPressed: (){
                            Navigator.push(context,
                                new MaterialPageRoute(
                                    builder: (context) => new Dep.Dependents(name: name,)
                                ));
                          },
                        ),
                  ],
                ),
              );
            } else if(snapshot.hasError){
                return new Text(snapshot.error);
            }

            return new Center(
              child: new CircularProgressIndicator(),
            );
          },
        ),
      ),
    );

    return new Scaffold(
      body: newbody,
    );
  }
}

crm_single_user_model.dart(Fourth.dart模型类)

class SingleUser{

  final String name, identification, company, dob;

  SingleUser({this.name, this.identification, this.company, this.dob});

  factory SingleUser.fromJson(Map<String, dynamic> ujson){

    return SingleUser(
      name: ujson["Name"].toString(),
      identification: ujson["Identification"].toString(),
      company: ujson["Company"].toString(),
      dob: ujson["DateOfBirth"].toString()
    );
  }
}

有什么方法可以在 Home.dart中 仅调用一次api,而不是每次我进入 Fourth.dart时都 重复发送新的读取请求吗?

非常感谢您的协助。


阅读 263

收藏
2020-08-13

共1个答案

一尘不染

问题出在您的build方法上,尤其是您要做的部分:

new FutureBuilder(
      future: fetchUser(),

基本上,如果您build因任何原因再次在何处被呼叫,您将fetchUser再次呼叫。


为什么要再次调用build? 我从来没有做过setState

setState这不是重建小部件的唯一方法。小部件可以重建的另一种情况是其父级更新(并创建了新的子实例)。

通常,您应该假定build可以随时调用。因此,您应该在那里做最少的工作。


为了解决这个问题,您应该将自己的fetchUser未来存储在您的州内。从调用initState。这将确保fetchUser在窗口小部件创建时仅调用一次。

class FourthState extends State<Fourth> {
  Future<SingleUser> userFuture;

  @override
  void initState() {
    userFuture = fetchUser();
    super.initState();
  }


   @override
   Widget build(BuildContext context) {
    return FutureBuilder(
      future: userFuture,
      builder: ...
    );
   }
}
2020-08-13