Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

dart - How to parse JSON only once in Flutter

I am making an app which takes values through JSON parsing. My app has multiple tabs but each time im swiping between tabs, the JSON sends a new read request every time. Below is my code:

Home.dart (Holds the navigation tab)

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 (One of the class which calls the 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 model class)

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()
    );
  }
}

Is there any way to call the api just once in Home.dart and not repeatedly send a new read request everytime i go into Fourth.dart?

Any assistance is very much appreciated.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You problem comes from your build method.Specifically the part where you do:

new FutureBuilder(
      future: fetchUser(),

Basically, if your build where to be called again for any reason, you would call fetchUser again.


Why build would be called again? I never did a setState

setState is not the only way a widget can get rebuilt. Another situation where a widget can get rebuilt is when its parent updates (and created a new child instance).

In general, you should assume that build can be called at any time. Therefore you should do the least amount of work there.


To solve this problem, you should store your fetchUser future inside your state. Called from the initState. This will ensure that the fetchUser is called only once at the widget creation.

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: ...
    );
   }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...