每 5 分钟或定期刷新 Flutter Text 小部件内容

Posted

技术标签:

【中文标题】每 5 分钟或定期刷新 Flutter Text 小部件内容【英文标题】:Refresh Flutter Text widget content every 5 minutes or periodically 【发布时间】:2019-05-23 23:15:02 【问题描述】:

我有一个 Flutter Text 小部件,其内容是通过外部 REST 调用填充的。我想通过调用 REST 端点每 5 分钟定期刷新小部件内容。

到目前为止,我设法每 5 分钟调用一次端点,但无法使用来自网络的新数据更新/刷新小部件内容。

 class PatientCount 
  int count;
  double amount;

PatientCount(this.count, this.amount);

 PatientCount.fromJson(Map<String, dynamic> map)
  : count = map['count'],
    amount = map['amount'];


Future<PatientCount> fetchPatientCount() async 

  var url = "http://localhost:9092/hms/patients-count-on-day";

  Map<String, String> requestHeaders = new Map<String, String>();
  requestHeaders["Accept"] = "application/json";
  requestHeaders["Content-type"] = "application/json";

  String requestBody = '"consultedOn":' + '16112018' + '';  

  http.Response response =
  await http.post(url, headers: requestHeaders, body: requestBody);

  final statusCode = response.statusCode;
  final Map responseBody = json.decode(response.body);

  if (statusCode != 200 || responseBody == null) 
     throw new FetchPatientCountException(
    "Error occured : [Status Code : $statusCode]");
   
    return PatientCount.fromJson(responseBody['responseData']. 
    ['PatientCountDTO']);
    
class MainPage extends StatefulWidget 
  @override
  _MainPageState createState() => _MainPageState();
 

class _MainPageState extends State<MainPage> 
@override
void initState() 
super.initState();
setState(() 
  const oneSecond = const Duration(seconds: 25);
  new Timer.periodic(oneSecond, (Timer t) => buildCountWidget());
);


@override
Widget build(BuildContext context) 
return Scaffold(
    appBar: AppBar(
      elevation: 2.0,
      backgroundColor: Colors.white,
      title: Text('Dashboard'),
    ),
    body: StaggeredGridView.count(
      crossAxisCount: 2,
      crossAxisSpacing: 12.0,
      mainAxisSpacing: 12.0,
      padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
      children: <Widget>[
        _buildTile(
          Padding(
            padding: const EdgeInsets.all(24.0),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        'Today\'s OPD',
                        style: TextStyle(
                            color: Colors.blueAccent, fontSize: 18.0),
                      ),
                      buildCountWidget(),
                    ],
                  ),
                  Material(
                      color: Colors.blue,
                      borderRadius: BorderRadius.circular(24.0),
                      child: Center(
                          child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Icon(Icons.timeline,
                            color: Colors.white, size: 30.0),
                      )))
                ]),
          ),
        ),
      ],
      staggeredTiles: [StaggeredTile.extent(2, 110.0)],
    ));
  

 Widget _buildTile(Widget child, Function() onTap) 
  return Material(
    elevation: 14.0,
    borderRadius: BorderRadius.circular(12.0),
    shadowColor: Color(0x802196F3),
    child: InkWell(
        // Do onTap() if it isn't null, otherwise do print()
        onTap: onTap != null
            ? () => onTap()
            : () 
                print('Not set yet');
              ,
        child: child));
  

 Widget buildCountWidget() 
 Widget vistitCount = new Center(
  child: new FutureBuilder<PatientCount>(
    future: fetchPatientCount(),
    builder: (context, snapshot)           
      if (snapshot.connectionState == ConnectionState.done) 
        if (snapshot.hasData) 
          print(snapshot.data.count);
          /* below text needs to be updated every 5 mins or so */
          return new Text('#' + snapshot.data.count.toString(),
              style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.w700,
                  fontSize: 34.0));
         else if (snapshot.hasError) 
          return new Text("$snapshot.error");
        
      

      // By default, show a loading spinner
      return new CircularProgressIndicator();
    ,
  ),
);
return vistitCount;


buildCountWidget 方法中,Text 小部件需要使用来自网络的最新数据进行刷新。

我将实现更改为使用 setState 如下,仍然没有运气

class _MainPageState extends State<MainPage> 
  Future<PatientCount> _patientCount;
  Timer timer;
  @override
  void initState() 
    super.initState();
    callApi();
    timer = Timer.periodic(Duration(seconds: 15), (Timer t) => setState(() ));
  

  void callApi() 
    setState(() 
      _patientCount = fetchPatientCount();
    );
  
 ..........................

还更改了如下逻辑,这样我可以调用 REST 端点,但小部件数据不会每 25 秒更新一次。小部件正在显示旧数据。

class _MainPageState extends State<MainPage> 
  Future<PatientCount> _patientCount;
  Timer timer;
  @override
  void initState() 
    super.initState();
    //callApi();
    timer = Timer.periodic(Duration(seconds: 15), (Timer t) => callApi());
  

  void callApi() 
    setState(() 
      _patientCount = fetchPatientCount();
    );
  

...........................

根据显示相同计数的代码,计数在 25 秒后不会增加。但是从后端定期触发 Api 并将数据返回到 UI,但小部件的状态没有改变。

【问题讨论】:

您不需要每次都构建小部件。只需将状态变量设置为文本并更新该变量的状态,它将反映为文本。 能否请您详细说明状态变量是什么意思? 【参考方案1】:

替换这个:

new Timer.periodic(oneSecond, (Timer t) => buildCountWidget());

通过这个:

new Timer.periodic(oneSecond, (Timer t) => setState(()));

它应该可以工作,每次调用 setState 时它都会刷新小部件并再次调用 Future 方法。

更新

它工作正常,如果您进行这些更改,您会注意到数据是如何刷新的(仅用于测试):

        Future<String> fetchPatientCount() async 
          print("fetchPatientCount");
          return DateTime.now().toIso8601String();
        

        ...

        new FutureBuilder<String>(
                future: fetchPatientCount(),
                builder: (context, snapshot) 
                  if (snapshot.connectionState == ConnectionState.done) 
                    if (snapshot.hasData)             
                      /* below text needs to be updated every 5 mins or so */
                      return new Text('#' + snapshot.data.toString(),
                          style: TextStyle(
                              color: Colors.black,
                              fontWeight: FontWeight.w700,
                              fontSize:7.0));
                     else if (snapshot.hasError) 
                      return new Text("$snapshot.error");
                    
                  

如果数据每 25 秒更改一次,则说明它有效,您必须检查您的 fetchPatientCount 方法。 (在发送requestBody之前将数据编码为json)

【讨论】:

上面提到的解决方案不起作用,数据没有重新加载,只显示旧数据。 不,我在获取数据时没有任何错误,端点正在返回预期值 检查我更新的答案:),如果它有效,那么你在获取数据时出错,不要忘记在发送到 http.post 之前对 requestBody 进行编码 用我的代码替换你的代码,让我知道它是否有效 感谢支持,我已经解决了问题。

以上是关于每 5 分钟或定期刷新 Flutter Text 小部件内容的主要内容,如果未能解决你的问题,请参考以下文章

定期删除重复的单元格

线程每5分钟刷新一次

AWS ElasticBeanstalk会定期关闭

PFQueryTableView 在新数据更新或每分钟使用 Parse 刷新时自动刷新

MS Excel - 如何每 5 秒自动刷新一次单元格?

将数据库缓存到每 5 分钟刷新一次的数组中