web-dev-qa-db-ja.com

5分ごとまたは定期的にFlutter Textウィジェットのコンテンツを更新する

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メソッド内で、テキストウィジェットをネットワークからの最新データで更新する必要があります。

以下のように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に返しますが、ウィジェットの状態は変化していません。

here the count is not changing every 25 seconds

3
isudarsan

これを交換してください:

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メソッドを確認する必要があります。 (送信する前にデータをjsonにエンコードしてくださいrequestBody

4
diegoveloper