如何将快照数据从 futurebuilder 传递到同一页面中的另一个 futurebuilder?

Posted

技术标签:

【中文标题】如何将快照数据从 futurebuilder 传递到同一页面中的另一个 futurebuilder?【英文标题】:How can i pass snapshot data from futurebuilder to another futurebuilder in the same page? 【发布时间】:2021-04-13 18:18:54 【问题描述】: 我是 Flutter 世界和移动应用程序开发的新手,并且正在为如何在我的应用程序中传递数据而苦苦挣扎。这是我的代码,如何将快照数据从 futurebuilder 传递到同一页面上的另一个 futurebuilder?请帮忙 **小部件 _buildProgrammCard()** 从这个小部件卡中,我需要将 futurebuilder 中的位置 ID 传递给另一个 futurebuilder。
Widget _buildProgrammCard() 
    return Container(
        height: 90,
       child:
            Card(
              semanticContainer: true,
              clipBehavior: Clip.antiAliasWithSaveLayer,
              color: Colors.white,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10.0),
              ),
              elevation: 4,
              margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
              child:
              FutureBuilder(
                  future: databaseHelper2.Lastlocation(),
                  builder: (context,snapshot) 
                    if (snapshot.hasError)
                    
                      print(snapshot.error);
                      print("there is problem");
                    
                    return snapshot.hasData
                        ?  Text("Location :" +snapshot.data.id)
                        :  Center(child: CircularProgressIndicator(
                    ),
                    );
                  
              ),
            ),
    );
  

小部件构建(BuildContext 上下文) 这是我需要将位置 ID 从另一个小部件传递给它的第二个小部件。

 Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: FutureBuilder(
              future: databaseHelper2.Getweither(location_id),
              builder: (context,snapshot) 
                if (snapshot.hasError)
                
                  print(snapshot.error);
                  print("there is problem !");
                
                return snapshot.hasData
                    ?  ItemList(list: snapshot.data)
                    :  Center(child: CircularProgressIndicator(
                ),
                );
              
          ),
    );
  

【问题讨论】:

【参考方案1】:

Flutter 经常重建小部件,因此 FutureBuilder 不应直接调用未来函数。 (小部件每秒最多可以调用其build 函数60 次。)

相反,FutureBuilder 应该只从其他地方调用的异步函数接收未来值。

在 StatefulWidget 中,启动长时间运行操作的最常见位置是在其 initState() 方法中。

在第一个 Widget initState 期间检索到的位置数据可以传递给第二个 Widget,就像常规的构造函数参数一样。

您将在第二个小部件的 State 类中使用 widget.locationId 访问它。

import 'package:flutter/material.dart';

class FirstFuturePage extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => FirstFutureState();


class FirstFutureState extends State<FirstFuturePage> 
  Future<int> locationId = Future.value(-1);

  @override
  void initState() 
    // TODO: implement initState
    super.initState();
    someAsyncCall();
  

  Future<void> someAsyncCall() async 
    // just returns the number 0 after 2 seconds & assigns it to "locationId" var
    locationId = Future.delayed(Duration(seconds: 2), () => 0);
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: FutureBuilder<int>(
            future: locationId,
            builder: (context, snapshot) 
              int _locationId = snapshot.data;
              if (snapshot.hasData)
                return SecondWidget(_locationId);
              return Text('Looking up location...');
            ,
          ),
        ),
      ),
    );
  


class SecondWidget extends StatefulWidget 
  final int locationId;

  SecondWidget(this.locationId);

  @override
  _SecondWidgetState createState() => _SecondWidgetState();


class _SecondWidgetState extends State<SecondWidget> 
  Future<String> weatherData = Future.value('Unknown');

  @override
  void initState() 
    super.initState();
    loadWeather(widget.locationId); // Use the locationId passed into widget
  

  /// Takes locationId from First widget and looks up weather data for location
  Future<void> loadWeather(int locationId) async 
    List<String> weatherDataStore = List<String>.from(['Rainy', 'Sunny']);
    weatherData = Future.delayed(
        Duration(seconds: 2), () => weatherDataStore[locationId]
    );
  

  @override
  Widget build(BuildContext context) 
    int _locId = widget.locationId;
    return FutureBuilder<String>(
      future: weatherData,
      builder: (context, snapshot) 
        if (snapshot.hasData) 
          return Text('Weather for location $_locId is: $snapshot.data');
        
        return Text('Loading Weather...');
      ,
    );
  

状态管理解决方案

当您厌倦了像上面示例中那样传递值时,您可以使用状态管理包或创建适合您需要的自己的包。

以下是 Jeff Delaney 关于各种选项的精彩概述: https://fireship.io/lessons/flutter-state-management-guide/

还可以查看上面未提及的 Get: https://pub.dev/packages/get

上面的一些状态管理解决方案(例如 Provider)帮助您正确使用 Flutter 原生状态功能(因为它相当复杂),而另一些则完全避免了这种情况,并提供了一个独立于 Widget 生命周期的框架(例如 Get)。

【讨论】:

感谢 Baker 的回复,但不是我的意思!我已经发布了另一个答案来向您解释更多。【参考方案2】:

感谢贝克的回复,但不完全是我的意思 这是我的两个未来和我的班级

这是我的未来,从她那里返回位置我需要将位置 ID 传递给另一个未来

Future<Location> Lastlocation() async 
    final prefs = await SharedPreferences.getInstance();
    final key = 'token';
    final value = prefs.get(key) ?? 0;
    String myUrl = "$serverUrl/location/getlastlocation?token=" + value;
    http.Response response = await http.get(
      myUrl,
      headers: 
        'Accept': 'application/json',
        //'Authorization': 'token $value'
      ,
    );
    if (response.statusCode == 200) 
      // If the server did return a 200 OK response,
      return Location.fromJson(json.decode(response.body));
     else 
      // then throw an exception.
      throw Exception('Failed to load album');
    
  

这是我的未来,返回取决于位置 ID 的天气列表

 Future<List> Getweither(String ID) async 
    final prefs = await SharedPreferences.getInstance();
    final key = 'token';
    final value = prefs.get(key) ?? 0;

    String myUrl = "$serverUrl/sensors/getDeviceByid/$ID?token=" + value;
    http.Response response = await http.get(myUrl,
        headers: 
          'Accept': 'application/json',
        );
    print("myUrldevice :"+myUrl);
    print("status :"+response.statusCode.toString());

    return json.decode(response.body);
  

这是我的班级位置

// To parse this JSON data, do
//
//     final location = locationFromJson(jsonString);

import 'dart:convert';

List<Location> locationFromJson(String str) => List<Location>.from(json.decode(str).map((x) => Location.fromJson(x)));

String locationToJson(List<Location> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Location 
  Location(
    this.automaticIrrigation,
    this.coordinates,
    this.createdDate,
    this.sensorIds,
    this.id,
    this.siteName,
    this.description,
    this.v,
  );

  bool automaticIrrigation;
  List<double> coordinates;
  DateTime createdDate;
  List<String> sensorIds;
  String id;
  String siteName;
  String description;
  int v;

  factory Location.fromJson(Map<String, dynamic> json) => Location(
    automaticIrrigation: json["AutomaticIrrigation"],
    coordinates: List<double>.from(json["Coordinates"].map((x) => x.toDouble())),
    createdDate: DateTime.parse(json["Created_date"]),
    sensorIds: List<String>.from(json["Sensor_ids"].map((x) => x)),
    id: json["_id"],
    siteName: json["SiteName"],
    description: json["Description"],
    v: json["__v"],
  );

  Map<String, dynamic> toJson() => 
    "AutomaticIrrigation": automaticIrrigation,
    "Coordinates": List<dynamic>.from(coordinates.map((x) => x)),
    "Created_date": createdDate.toIso8601String(),
    "Sensor_ids": List<dynamic>.from(sensorIds.map((x) => x)),
    "_id": id,
    "SiteName": siteName,
    "Description": description,
    "__v": v,
  ;

这是我的主页

import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sidebar_animation/Services/DataHelpers.dart';
import 'package:sidebar_animation/sidebar/sidebar_layout.dart';
import '../bloc.navigation_bloc/navigation_bloc.dart';
import 'package:sidebar_animation/constants.dart';
import 'package:flutter/gestures.dart';
import 'package:sidebar_animation/bloc.navigation_bloc/navigation_bloc.dart';

class HomePage extends StatelessWidget with NavigationStates 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 


  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
   DatabaseHelper2 databaseHelper2 = new DatabaseHelper2();
  @override
  void initState() 
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Colors.grey[100],
      body: ListView(
          FutureBuilder(
              future: databaseHelper2.Getweither(location_id),
              builder: (context,snapshot) 
                if (snapshot.hasError)
                
                  print(snapshot.error);
                  print("there is problem !");
                
                return snapshot.hasData
                    ?  ItemList(list: snapshot.data)
                    :  Center(child: CircularProgressIndicator(
                ),
                );
              
),
    );
  

  Widget _buildProgrammCard() 
    return Container(
        height: 90,
       child:
            Card(
              semanticContainer: true,
              clipBehavior: Clip.antiAliasWithSaveLayer,
              color: Colors.white,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10.0),
              ),
              elevation: 4,
              margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
              child:
              FutureBuilder(
//                future: databaseHelper.getData(),
                  future: databaseHelper2.Lastlocation(),
                  builder: (context,snapshot) 
                    if (snapshot.hasError)
                    
                      print(snapshot.error);
                      print("mochkla lenaa *");
                    
                    return snapshot.hasData
                        ?  Text("Location :" +snapshot.data.siteName)
                        :  Center(child: CircularProgressIndicator(
                    ),
                    );
                  
              ),
            ),

    );

  

class ItemList extends StatelessWidget
  List list;
  ItemList(this.list);

  ScrollController _controller = new ScrollController();


  @override
  Widget build(BuildContext context) 
    return  ListView.builder(
      itemCount: list == null ? 0 : list.length,
      scrollDirection: Axis.horizontal,
      itemExtent: 190.0,
      itemBuilder: (context, index) 
        return Padding(
          padding: const EdgeInsets.fromLTRB(10, 0, 0, 14),
          child: Container(
            decoration: BoxDecoration(
              image: DecorationImage(
                image: NetworkImage(
                  item.storyUrl,
                ),
                fit: BoxFit.cover,
                colorFilter: ColorFilter.mode(
                  Colors.black26,
                  BlendMode.darken,
                ),
              ),
              borderRadius: BorderRadius.circular(10.0),
              color: Colors.grey,
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Text(
                    "temp :",
                    style: TextStyle(color: Colors.white),
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(left: 24),
                  child:
                  Text(
                    list[i]['Weither']['Temp'],
                  ),
                ),
              ],
            ),
          ),
        );
      ,
    ),

  

最后我需要从第一个 future 中获取 location id,将 Location 返回到第二个 future Getweither(String ID)

【讨论】:

嗨 Dhouib,感谢您提供的额外细节。我希望能够引导您朝着在 Flutter 中公开通用设计模式的方向发展,并让您通过更改当前架构来学习/发现直接解决问题的方法。这里有另一个建议:使 _buildProgrammCard 成为它自己的 StatefulWidget 类,这样它就可以在创建时在其 initState 方法中启动 Lastlocation 调用。 (我假设您需要 Lastlocation 数据才能获取天气。) 感谢@baker 的评论。这很有帮助。

以上是关于如何将快照数据从 futurebuilder 传递到同一页面中的另一个 futurebuilder?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Mockito 的 FutureBuilder 快照数据为空 - Flutter

将异步数据(GPS 协调)从 FutureBuilder 传递给提供者/设置异步构造函数

Flutter Futurebuilder 快照为空

将快照数据从一个 dart 文件传递​​到颤振项目中的另一个文件

如何在 FutureBuilder Flutter 中停止循环 SetState

如何在 FutureBuilder 中解析从本地主机接收的 JSON 对象