如果在获取图像时关闭网络,Future Builder 中的空白区域会颤动

Flutter Empty Spaces in Future Builder if you turned off network while fetching images

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

我想做的是拉起重建所有图像请注意,我已经实现了 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() 但它没有重建网格视图

建议的解决方案:如果您可以找到以编程方式热重载应用程序的功能,那么热重载正确地完成了所需的事情,那么我认为这将解决问题

更新: 对于将来会遇到同样问题的任何人 恐怕我的问题没有直接的答案所以我给了我个人观点最好的

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

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

伪代码:

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

像这样的东西只会在有互联网连接时获取公司。

不要在没有互联网连接的情况下空 space,而是尝试使用 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
);