【问题标题】:Flutter Empty Spaces in Future Builder if you turned off network while fetching images如果您在获取图像时关闭网络,Future Builder 中的空白空间会颤动
【发布时间】:2020-10-20 01:22:07
【问题描述】:

你好,我有一个简单的应用程序,它从网络服务器获取图像,然后在网格视图构建器中显示它们

我正在尝试做的是上拉重建所有图像注意我已经实现了 RefreshIndicator() 及其 OnRefresh 功能在数据库发生更改时正常工作,它将新图像添加到视图中,

我还希望它重建所有图像,因为和我一起想象一下这种情况:

您打开了应用程序,未来的构建器获取了数据,现在它正在显示它们,但是当它显示它们时,您突然与互联网断开连接,这将给您留下空白空间(它应该是图像,但现在它们是空白的) 所以我想做的是在上拉时重建这些图像,这样如果用户发现任何丢失的图像,他就可以拉起来刷新页面并再次重建所有图像

这是我的代码,只是 RefreshCompanies() 需要修改

import 'dart:convert';
import 'package:app/exceptions/connection_error.dart';
import 'package:app/exceptions/empty_db.dart';
import 'package:app/reusable_widgets/interfaces/Companies/Companies_interface.dart';
import 'package:app/reusable_widgets/interfaces/LoadingIndicator.dart';
import 'package:app/reusable_widgets/interfaces/Main_Layout.dart';
import 'package:app/reusable_widgets/logic/Check_version.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:http/http.dart' as http;
import 'Categories.dart';
import 'package:app/reusable_widgets/globals.dart';

class Companies {
  final int id;
  final String name;
  final String companyLogo;

  Companies({this.id, this.name, this.companyLogo});

  factory Companies.fromJson(Map<String, dynamic> json) {
    return Companies(
      id: json['id'],
      name: json['name'],
      companyLogo: json['company_logo'],
    );
  }
}

Future<List<Companies>> fetchCompanies() async {
  final response = await http.get('$webSiteUrl/company/api/fetch');
  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return parseCompanies(response.body);
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load the companies');
  }
}


List<Companies> parseCompanies(String responseBody) {
  final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
  return parsed.map<Companies>((json) => Companies.fromJson(json)).toList();
}

class CompaniesPage extends StatefulWidget{
  @override
  _CompaniesState createState() => _CompaniesState();
}

class _CompaniesState extends State<CompaniesPage> {
  Future<List<Companies>> _companies;
  var refreshKey = GlobalKey<RefreshIndicatorState>();
  @override
  void initState() {
    super.initState();
    checkVersion(context);
    _companies = fetchCompanies();
  }

  Future<Null> refreshCompanies() async{
    await DefaultCacheManager().emptyCache();
    await Future.delayed(Duration(seconds: 2)).then((value) =>
        setState(() {
          _companies = fetchCompanies();
        })
    );
  }

  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FutureBuilder<List<Companies>>(
          future: _companies,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              List<Companies> companies = snapshot.data;
              if(companies.length >= 1){
                return MainLayout(
                  RefreshIndicator(
                    onRefresh: refreshCompanies,
                    key: refreshKey,
                    child: GridView.count(
                      crossAxisCount: 2 ,
                      children: List.generate(companies.length, (index) {
                        return GestureDetector(
                          onTap: () => {
                            Navigator.push(
                              context,
                              MaterialPageRoute(builder: (context) => Categories(companies[index].id, companies[index].name)),
                            )},
                          child: CompaniesInterface(companies[index].id , companies[index].name , companies[index].companyLogo),
                        );
                      }),
                    ),
                  ),
                );
              }else{
                return EmptyDataBase();
              }
            } else if (snapshot.hasError) {
              return ConnectionError();
            }

            // By default, show a loading spinner.
            return LoadingIndicator();
          },
        ),
      ),
    );
  }
}

我的公司界面

class CompaniesInterface extends StatelessWidget{
  final companyId;
  final companyName;
  final companyLogo;

  CompaniesInterface(this.companyId , this.companyName ,this.companyLogo);
  @override
  Widget build(BuildContext context) {
    return Align(
      child : Container(
        margin: EdgeInsets.all(20),
        height: DeviceInformation(context).height * 0.7,
        width: DeviceInformation(context).width * 0.9,
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(20),
          shape: BoxShape.rectangle,
          image: DecorationImage(
            image: NetworkImage('$webSiteUrl$companyLogo'),
            fit: BoxFit.fill,
          ),
        ),
      ),
    );
  }
}

在这里你可以找到我所说的空格

从我尝试调用 EmptyCache() 的代码中可以看出,它没有重建网格视图

建议的解决方案:如果您可以找到以编程方式热重载应用程序的功能,那么热重载正在正确执行所需的操作,那么我认为这将解决问题

更新: 对于将来会带着同样问题来的人 恐怕我的问题没有直接的答案,所以我授予了我认为最好的答案

【问题讨论】:

  • 为什么不用缓存网络图片呢?它可以让您灵活地为每个图像显示加载器,并在重新加载页面时节省一些带宽,因为它会缓存图像以供将来使用。此外,如果图像无法加载,它会给您一个错误,您可以使用它来相应地更新 UI。
  • 我正在考虑使用它,但现在不行,我稍后会使用它

标签: api flutter gridview


【解决方案1】:

FutureBuilder 只会运行一次。在 Future 它跟踪完成后,它将不再重建。您必须调用setState() 或将其转换为Stream,在数据刷新时添加新数据。

【讨论】:

  • 好吧,我已经尝试将其转换为流并使用 setstate 添加它,但它没有做到这一点,因为它只添加或删除基于数据库的数据,所以流没有工作,然后我尝试与未来的构建器一起做它做了同样的工作它只是删除已删除的数据并将新数据添加到屏幕它没有刷新整个页面
  • 对不起,我没听懂
  • 请与我一起考虑这种情况以获得更多说明。用户获取了数据,现在数据正在应用程序中显示给他,现在我作为网站管理员在我的服务器中添加了一些数据。现在用户拉起,应用程序显示刷新指示器并使用 setState 将新数据添加到变量中。将会发生的是,Future Builder 或 Stream Builder 只会获取新数据并将它们添加到屏幕,它不会重建任何丢失的元素
  • 为什么不重建缺失的元素?如果你设置正确,它将重建。您必须将新数据、服务器中的项目列表添加到 Stream,然后 StreamBuilder 将做出反应并重建所有内容。您可能需要使用Keys 来确保一切正常。
  • 你可能想看看这里stackoverflow.com/questions/62604261/…我之前问过的这个问题,如果我正确理解你的话,它就像你告诉我的那样,但即使流构建器没有重建
【解决方案2】:

不确定这是否对您有用,但也许您只能在有活动连接时获取图像。 https://pub.dev/packages/connectivity

伪代码:

Stream<List<Companies>> $ companiesStream = $connectivitySteam()
  .filter(x => x.hadInternet)
  .take(1)
  .switchMap(x => fetchCompanies)

这样的事情只会在有互联网连接时获取公司。

【讨论】:

  • 只有当您的互联网连接较弱或您在未来的构建者正在构建资产时突然断开互联网连接时才会出现问题,我只想重新加载页面或删除图像并重新获取它们将作为好吧,我被这个问题困了 5 天 XD
【解决方案3】:

不要在没有互联网连接的情况下留出空白空间,而是尝试使用cached_network_image 作为图像小部件,它为您提供更多控件,如占位符(如果没有连接,您仍然可以显示加载图像):

CachedNetworkImage(
  imageUrl: "http://via.placeholder.com/200x150",
  imageBuilder: (context, imageProvider) => Container(
    decoration: BoxDecoration(
      image: DecorationImage(
          image: imageProvider,
          fit: BoxFit.cover,
          colorFilter:
              ColorFilter.mode(Colors.red, BlendMode.colorBurn)),
    ),
  ),
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
),

或者,如果没有连接,您甚至可以使用 flutter_advanced_networkimage 刷新您想要的每一个图像:

TransitionToImage(
  image: AdvancedNetworkImage(url,
    loadedCallback: () {
      print('It works!');
    },
    loadFailedCallback: () {
      print('Oh, no!');
    },
    loadingProgress: (double progress) {
      print('Now Loading: $progress');
    },
  ),
  loadingWidgetBuilder: (_, double progress, __) => Text(progress.toString()),
  fit: BoxFit.contain,
  placeholder: const Icon(Icons.refresh),
  width: 400.0,
  height: 300.0,
  enableRefresh: true, // <-- Refresh button
);

【讨论】:

  • 这是一种很好的方法,我将很快使用这种方法重构我的代码,但很抱歉,这不能回答问题,这是一个替代方案,但非常感谢您的回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
  • 1970-01-01
相关资源
最近更新 更多