如何在颤动中加载数据之前显示进度对话框?

Posted

技术标签:

【中文标题】如何在颤动中加载数据之前显示进度对话框?【英文标题】:How to show a progress Dialog before data loading in flutter? 【发布时间】:2019-12-01 15:38:30 【问题描述】:

在我的 Flutter 项目中,我正在执行 API 调用以通过 GET 请求获取数据。从响应中解析出 JSON 对象 后,我只在 Text 小部件中显示该值。虽然加载数据需要时间,但与此同时,我的 Text 小部件显示为 null。

对于 API 调用部分,我有以下代码-

class Webservice 
  Future<T> load<T>(Resource<T> resource) async 
    var jwt = await LocalStore().getJWT();
    print(jwt);

    final response = await http.get(resource.url,
        headers: 
          'Content-Type': 'application/json',
          'token': '$Constants.TOKEN',
          'jwt': '$jwt',
        
    );
    if(response.statusCode == 200) 
      print('$response.body');
      return resource.parse(response);
     else 
      throw Exception('Failed to load data!');
    
  

我为 JSON 解析做了一个 Model 类-

class Category 
  int catNote;
  int catTodo;
  int catRem;
  int catTag;
  int catUrgent;
  int catWork;
  int catOffice;
  int catPersonal;
  
  Category(
      this.catNote,
        this.catTodo,
        this.catRem,
        this.catTag,
        this.catUrgent,
        this.catWork,
        this.catOffice,
        this.catPersonal);

  Category.fromJson(Map<String, dynamic> json) 
    catNote = json['cat_note'];
    catTodo = json['cat_todo'];
    catRem = json['cat_rem'];
    catTag = json['cat_tag'];
    catUrgent = json['cat_urgent'];
    catWork = json['cat_work'];
    catOffice = json['cat_office'];
    catPersonal = json['cat_personal'];
  

  Map<String, dynamic> toJson() 
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['cat_note'] = this.catNote;
    data['cat_todo'] = this.catTodo;
    data['cat_rem'] = this.catRem;
    data['cat_tag'] = this.catTag;
    data['cat_urgent'] = this.catUrgent;
    data['cat_work'] = this.catWork;
    data['cat_office'] = this.catOffice;
    data['cat_personal'] = this.catPersonal;
    return data;
  

  static Resource<Category> get allCategory 
    return Resource(
        url: '$Constants.BASE_URLcategory',
        parse: (response) 
          print('my result $response.body');
          final result = json.decode(response.body);

          Category category = Category.fromJson(result) ;
          return category;

        
    );

  


现在,在我的主课中,我创建了一个如下所示的函数-

  void _getAllCategories() 
    Webservice().load(Category.allCategory).then((newsArticles) => 
        setState(() => 
      _category = newsArticles
    )
  );
 

之后,我在 initState 函数中调用了该函数,并将值保存在 _category 对象中。

然后在文本小部件的 Widget build(BuildContext context) 函数中,我使用了来自 _category 对象的值,如下所示,使用三元运算符来检查对象是否是是否为空。如果为空,则应显示 0,如果不为空,则应显示原始值-

child: _category ==null?
                Text('0',
                style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold
                ),
                ):
                Text('$_category.catToDo',
                  style: TextStyle(
                      fontSize: 30,
                      fontWeight: FontWeight.bold
                  ),
                )

但它仍然显示空值,而数据加载需要几秒钟,并显示如下输出-

所以,我需要一个解决方案来显示进度对话框,或者在数据加载需要时间时将默认值显示为 0。如果有人帮助我编写这段代码,那就太好了。

【问题讨论】:

Future.builder 类 - api.flutter.dev/flutter/widgets/FutureBuilder-class.html 我在从 api 获取文本字段中的数据时遇到同样的问题 【参考方案1】:

在加载期间使用 FutureBuilder 控制渲染;

  final categories = Webservice().load(Category.allCategory);

  Widget build(BuildContext context) 
    return FutureBuilder(
      future: categories,
      builder: (ctx, snapshot) 
        var value = (snapshot.connectionState == ConnectionState.done) ? '$_category.catToDo' : '0';

        return Text(
          value,
          style: TextStyle(
            fontSize: 30,
            fontWeight: FontWeight.bold
          ),
        );
      
    );
  

或者如果你想显示加载动画:

  final categories = Webservice().load(Category.allCategory);

  Widget build(BuildContext context) 
    return FutureBuilder(
      future: categories,
      builder: (ctx, snapshot) 
        if (snapshot.connectionState == ConnectionState.done) 
          return Text(
            '$_category.catToDo',
            style: TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold
            ),
          );
        
        else 
          return CircularProgressIndicator();
        
      
    );
  

【讨论】:

【参考方案2】:

您可以检查this 包以显示具有不同样式的加载旋转。

之后你需要使用Future Builder小部件

这是一个如何与 spinkit 一起使用的示例

FutureBuilder(
        future: myAwesomeFutureMethod(), // you should put here your method that call your web service
        builder:

            (BuildContext context, AsyncSnapshot<List<BillResponse>> snapshot)  
            /// The snapshot data type have to be same of the result of your web service method
          if (snapshot.hasData) 
            /// When the result of the future call respond and has data show that data
            return SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: bodyData(snapshot.data),
            );
          
          /// While is no data show this
          return Center(
            child: SpinKitDualRing(
              color: Colors.blue,
            ),
          );
        ,
      ),

希望这会有所帮助。干杯。

【讨论】:

【参考方案3】:

您应该使用FutureBuilder 类来执行此操作。

此示例显示了一个FutureBuilder,它在加载数据时显示加载微调器。如果 Future 以结果完成,则显示成功图标和文本,如果 Future 以错误完成,则显示错误图标和文本。假设 _calculation 字段是通过按下 UI 中其他位置的按钮来设置的。

这是一个例子

class MyStatefulWidget extends StatefulWidget 
  MyStatefulWidget(Key key) : super(key: key);

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


/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> 
  Future<String> _calculation = Future<String>.delayed(
    Duration(seconds: 2),
    () => 'Data Loaded',
  );

  Widget build(BuildContext context) 
    return DefaultTextStyle(
      style: Theme.of(context).textTheme.headline2,
      textAlign: TextAlign.center,
      child: FutureBuilder<String>(
        future: _calculation, // a previously-obtained Future<String> or null
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) 
          List<Widget> children;
          if (snapshot.hasData) 
            children = <Widget>[
              Icon(
                Icons.check_circle_outline,
                color: Colors.green,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Result: $snapshot.data'),
              )
            ];
           else if (snapshot.hasError) 
            children = <Widget>[
              Icon(
                Icons.error_outline,
                color: Colors.red,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Error: $snapshot.error'),
              )
            ];
           else 
            children = <Widget>[
              SizedBox(
                child: CircularProgressIndicator(),
                width: 60,
                height: 60,
              ),
              const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text('Awaiting result...'),
              )
            ];
          
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: children,
            ),
          );
        ,
      ),
    );
  

【讨论】:

【参考方案4】:

确保_category 为空,也许您在加载数据之前为其分配了一个值。

【讨论】:

【参考方案5】:

如果你想显示 Cellular bar 进度指示器,下面是它的演示:

导入'package:flutter/material.dart';

void main() 
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MyWidget(),
    ),
  );


class MyWidget extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Colors.blueGrey,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            BarProgressIndicator(
              numberOfBars: 4,
              color: Colors.white,
              fontSize: 5.0,
              barSpacing: 2.0,
              beginTweenValue: 5.0,
              endTweenValue: 13.0,
              milliseconds: 200,
            ),
            Text(
              'Cellular bar progress indicator',
              style: TextStyle(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  


class BarProgressIndicator extends StatefulWidget 
  final int numberOfBars;
  final double fontSize;
  final double barSpacing;
  final Color color;
  final int milliseconds;
  final double beginTweenValue;
  final double endTweenValue;

  BarProgressIndicator(
    this.numberOfBars = 3,
    this.fontSize = 10.0,
    this.color = Colors.black,
    this.barSpacing = 0.0,
    this.milliseconds = 250,
    this.beginTweenValue = 5.0,
    this.endTweenValue = 10.0,
  );

  _BarProgressIndicatorState createState() => _BarProgressIndicatorState(
        numberOfBars: this.numberOfBars,
        fontSize: this.fontSize,
        color: this.color,
        barSpacing: this.barSpacing,
        milliseconds: this.milliseconds,
        beginTweenValue: this.beginTweenValue,
        endTweenValue: this.endTweenValue,
      );


class _BarProgressIndicatorState extends State<BarProgressIndicator>
    with TickerProviderStateMixin 
  int numberOfBars;
  int milliseconds;
  double fontSize;
  double barSpacing;
  Color color;
  double beginTweenValue;
  double endTweenValue;
  List<AnimationController> controllers = new List<AnimationController>();
  List<Animation<double>> animations = new List<Animation<double>>();
  List<Widget> _widgets = new List<Widget>();

  _BarProgressIndicatorState(
    this.numberOfBars,
    this.fontSize,
    this.color,
    this.barSpacing,
    this.milliseconds,
    this.beginTweenValue,
    this.endTweenValue,
  );

  initState() 
    super.initState();
    for (int i = 0; i < numberOfBars; i++) 
      _addAnimationControllers();
      _buildAnimations(i);
      _addListOfDots(i);
    

    controllers[0].forward();
  

  void _addAnimationControllers() 
    controllers.add(AnimationController(
        duration: Duration(milliseconds: milliseconds), vsync: this));
  

  void _addListOfDots(int index) 
    _widgets.add(Padding(
      padding: EdgeInsets.only(right: barSpacing),
      child: _AnimatingBar(
        animation: animations[index],
        fontSize: fontSize,
        color: color,
      ),
    ));
  

  void _buildAnimations(int index) 
    animations.add(
        Tween(begin: widget.beginTweenValue, end: widget.endTweenValue)
            .animate(controllers[index])
              ..addStatusListener((AnimationStatus status) 
                if (status == AnimationStatus.completed)
                  controllers[index].reverse();
                if (index == numberOfBars - 1 &&
                    status == AnimationStatus.dismissed) 
                  controllers[0].forward();
                
                if (animations[index].value > widget.endTweenValue / 2 &&
                    index < numberOfBars - 1) 
                  controllers[index + 1].forward();
                
              ));
  

  Widget build(BuildContext context) 
    return SizedBox(
      height: 30.0,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.center,
        children: _widgets,
      ),
    );
  

  dispose() 
    for (int i = 0; i < numberOfBars; i++) controllers[i].dispose();
    super.dispose();
  


class _AnimatingBar extends AnimatedWidget 
  final Color color;
  final double fontSize;
  _AnimatingBar(
      Key key, Animation<double> animation, this.color, this.fontSize)
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) 
    final Animation<double> animation = listenable;
    return Container(
      height: animation.value,
      decoration: BoxDecoration(
        shape: BoxShape.rectangle,
        border: Border.all(color: color),
        borderRadius: BorderRadius.circular(2.0),
        color: color,
      ),
      width: fontSize,
    );
  

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review

以上是关于如何在颤动中加载数据之前显示进度对话框?的主要内容,如果未能解决你的问题,请参考以下文章

从mysql的片段中加载ListView

如何在 Android 中开始活动之前显示进度对话框?

Jquery 对话框未在 aspnet MVC 中加载

easyui中加载两个对话框画面显示错误

如何在android中的对话框中加载webview

在 UI 对话框中加载 iframe 不起作用