简体   繁体   English

FutureBuilder 在调试阶段工作正常,但在 apk 构建上总是抛出错误

[英]FutureBuilder works fine on the debug stage but on apk build always throws error

I have the following code which should show a process indicator during the process of fetching a JSON data from the web.我有以下代码,它应该在从 Web 获取 JSON 数据的过程中显示一个进程指示器。 It works fine when I run in the debug mode.当我在调试模式下运行时它工作正常。 But as soon as I make it to an apk, the app opens and with no time delays, it throws this error:但是,一旦我将其制作为 apk,该应用程序就会打开并且没有时间延迟,它会引发此错误:

SocketException:套接字异常:

How to get rid of these issues?如何摆脱这些问题?

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Quize It Up'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    getCategories();
  }

  Future<List> getCategories() async {
    var url = 'https://opentdb.com/api_category.php';
    var response = await http
        .get(Uri.encodeFull(url), headers: {"Accept": "application/json"});

    var jsonResponse = convert.jsonDecode(response.body);
    List categories = jsonResponse["trivia_categories"];

    return categories;
  }

  Widget mainGui() {
    return Container(
        child: FutureBuilder(
            future: getCategories(),
            builder: (BuildContext context, AsyncSnapshot<List> asyncSnapshot) {
              switch (asyncSnapshot.connectionState) {
                case ConnectionState.active:
                case ConnectionState.waiting:
                  return Center(
                    child: CircularProgressIndicator(),
                  );
                case ConnectionState.none:
                  return RaisedButton(
                    onPressed: () => getCategories(),
                  );
                case ConnectionState.done:
                  if (asyncSnapshot.hasData)
                    return Text(asyncSnapshot.error.toString());
                  else
                    return ListView.builder(
                      itemCount: asyncSnapshot.data.length,
                      itemBuilder: (context, int index) {
                        return ListTile(
                          title: Text(asyncSnapshot.data[index]['name']),
                        );
                      },
                    );
              }
            }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        child: mainGui(),
      ),
    );
  }
}

First of all you are calling getCategories();首先,您正在调用getCategories(); in your initState() method for no reason.在您的initState()方法中无缘无故。 Since getCategories() is simply building and returning a Future that you don't use in initState this call is irrelevant.由于getCategories()只是构建并返回您不在 initState 中使用的 Future,因此此调用无关紧要。 You might simply delete this line or even the entire initState() method.您可以简单地删除这一行甚至整个initState()方法。

Then this code basically does nothing:那么这段代码基本上什么都不做:

case ConnectionState.none:
   return RaisedButton(
      onPressed: () => getCategories(),
   );

In order to show a button that reloads the Future you might instead refresh the state like this.为了显示一个重新加载 Future 的按钮,您可能会像这样刷新状态。 This will rebuild your widget and therefore rebuild the future.这将重建您的小部件,从而重建未来。

case ConnectionState.none:
   return RaisedButton(
      onPressed: () {
         setState(() {});
      },
   );

But anyways.但无论如何。 If all that you want to achieve is a loading indicator while the Future is loading I'd rather observe the hasData attribute of the snapshot.如果您想要实现的只是在 Future 加载时加载指示器,我宁愿观察快照的hasData属性。 Something like this might work:像这样的事情可能会奏效:

return FutureBuilder (
        future: getCategories(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (snapshot.hasData) {
            if (snapshot.data) {
              return ListView.builder(
                itemCount: asyncSnapshot.data.length,
                itemBuilder: (context, int index) {
                  return ListTile(
                    title: Text(asyncSnapshot.data[index]['name']),
                  );
                },
              );
            }
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        }
    );

Just for clarity.只是为了清楚。 The error you are seeing is coming from this bug here:您看到的错误来自这里的错误:

case ConnectionState.done:
   if (asyncSnapshot.hasData)
      return Text(asyncSnapshot.error.toString());

Your saying here that if the Future is done and has some data received then you show the error instead of the data.您在这里说的是,如果 Future 完成并收到了一些数据,那么您将显示错误而不是数据。

The solution was to add解决方案是添加
<uses-permission android:name="android.permission.INTERNET"/>

in the manifest.xml file as the latest flutter SDK has changed the project structure.在 manifest.xml 文件中,因为最新的 Flutter SDK 改变了项目结构。

是的,解决方案是添加<uses-permission android:name="android.permission.INTERNET"/> ,但您必须将其添加到 Flutter 项目中 /android/src/main 位置的 AndroidManifest.xml 文件中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM