如何在 Flutter 中只解析一次 JSON

Posted

技术标签:

【中文标题】如何在 Flutter 中只解析一次 JSON【英文标题】:How to parse JSON only once in Flutter 【发布时间】:2019-01-24 00:26:47 【问题描述】:

我正在制作一个通过 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 时重复发送新的读取请求?

非常感谢任何帮助。

【问题讨论】:

【参考方案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: ...
    );
   

【讨论】:

谢谢。这个答案是正确的。在第一次调用 initState() 后,它不再加载一次。

以上是关于如何在 Flutter 中只解析一次 JSON的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:如何解析超复杂的 json 文件?

如何使用从 Flutter 中的 json 解析的嵌套映射列表中的函数创建对象

如果 Flutter/Dart 中没有名称/键,如何序列化/解析嵌套的 JSON 数组

如何在 Flutter 中只选择一组单选按钮的值?

Flutter:Json解析

如何将flutter中的日期解析为与Java ZonedDateTime兼容的json字符串