質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

1回答

1379閲覧

[Flutter]非同期処理に対してStreamBuilderを使って更新を即時反映したい

asapan

総合スコア60

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2022/12/31 04:53

前提

Flutterのアプリ開発の勉強をしています。(Flutter開発を始めたばかりです)
ローカル通知の機能を試しに実装をしていて、何の通知が登録されているかを表示しようとしています。

画面と機能としては以下の通りです

  • 今すぐ通知(クリックすると通知が即座に表示されます)
  • 通知をスケジュール(クリックすると60分後に通知が表示されます)
  • スケジュールされた通知をすべてキャンセル(クリックすると登録された通知が削除されます)
  • 画面中央から下部(登録された通知をテキスト形式で表示しています)

イメージ説明

実現したいこと

実現したいことは以下の通りです。

  • 「通知をスケジュール」を押すと、下のテキスト部分に、通知の情報が追加で表示されてほしいです。

    • 現状だと、他のページに移ってから戻らないと更新されないので困っています。
  • またテキスト形式でなくListViewなどで登録されている通知を一つ一つ表示する方法もわからないので教えてほしいです。

該当のソースコード

画面に対するソースコード

Dart

1import 'package:flutter/material.dart'; 2import './notifications_utils.dart'; 3 4class AccountPage extends StatefulWidget { 5 const AccountPage({super.key}); 6 7 8 State<AccountPage> createState() => _AccountPageState(); 9} 10 11class _AccountPageState extends State<AccountPage> { 12 late Stream<String> data; 13 14 15 void initState() { 16 data = NotificationsUtils.getScheduled(); 17 super.initState(); 18 } 19 20 21 Widget build(BuildContext context) { 22 return Scaffold( 23 appBar: AppBar( 24 title: const Text("Account page"), 25 ), 26 body: Center( 27 child: Column( 28 children: [ 29 // 今すぐ通知 30 TextButton( 31 onPressed: () { 32 NotificationsUtils.notifyNow(); 33 }, 34 child: const Text('今すぐ通知'), 35 ), 36 37 // 通知をスケジュール 38 TextButton( 39 onPressed: () { 40 NotificationsUtils.scheduleNotifications( 41 DateTime.now().add(const Duration(seconds: 60)), 42 ); 43 }, 44 child: const Text('通知をスケジュール'), 45 ), 46 47 // スケジュールされた通知をすべてキャンセル 48 TextButton( 49 onPressed: () { 50 NotificationsUtils.cancelNotificationsSchedule(); 51 }, 52 child: const Text('スケジュールされた通知をすべてキャンセル'), 53 ), 54 // 登録された通知を表示 55 StreamBuilder( 56 stream: data, 57 builder: (BuildContext context, AsyncSnapshot<String> snapshot) { 58 if (snapshot.hasData) { 59 return Text(snapshot.data!); 60 } else if (snapshot.hasError) { 61 return Text('${snapshot.error!}'); 62 } else { 63 return const CircularProgressIndicator(); 64 } 65 } 66 ) 67 ], 68 ), 69 ), 70 ); 71 } 72} 73

通知に関するソースコード

import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:timezone/timezone.dart' as tz; class NotificationsUtils { // 今すぐ通知する static Future<void> notifyNow() async { final flnp = FlutterLocalNotificationsPlugin(); flnp.show( 0, '手動通知', 'あなたがボタンをタップしました', const NotificationDetails( android: AndroidNotificationDetails( '1', '手動通知', importance: Importance.high, priority: Priority.high, ), iOS: DarwinNotificationDetails( presentSound: true, ), ), ); } // 通知をスケジュール static Future<void> scheduleNotifications(DateTime dateTime, {DateTimeComponents? dateTimeComponents}) async { // idを動的に生成 String id = '${dateTime.hour}${dateTime.minute}'; // 日時をTimeZoneを考慮した日時に変換する final scheduleTime = tz.TZDateTime.from(dateTime, tz.local); // 通知をスケジュールする final flnp = FlutterLocalNotificationsPlugin(); await flnp.zonedSchedule( int.parse(id), 'スケジュール通知', 'あなたがスケジュールした時間になりました', scheduleTime, const NotificationDetails( android: AndroidNotificationDetails( '2', 'スケジュール通知', importance: Importance.high, priority: Priority.high, ), iOS: DarwinNotificationDetails( presentSound: true, ), ), uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, androidAllowWhileIdle: true, matchDateTimeComponents: DateTimeComponents.time, ); } // 登録されている通知の確認 static Stream<String> getScheduled() async* { final flnp = FlutterLocalNotificationsPlugin(); final List<PendingNotificationRequest> pendingNotificationRequests = await flnp.pendingNotificationRequests(); String str = ''; for (var pendingNotificationRequest in pendingNotificationRequests) { str += '予約済みの通知: [id: ${pendingNotificationRequest.id}, title: ${pendingNotificationRequest.title}, body: ${pendingNotificationRequest.body}, payload: ${pendingNotificationRequest.payload}] ¥n'; } yield str; } // 通知をキャンセル static Future<void> cancelNotificationsSchedule() async { final flnp = FlutterLocalNotificationsPlugin(); await flnp.cancelAll(); } }

試したこと

上記のコードでは上手くいきませんでした。
お力添えよろしくお願いします。

補足

通知はここを参考に実装しました
https://hondakenya.work/flutter-local-notifications-sample/

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

pendingNotificationRequestsは、未通知情報を取得するメソッドなので、それをList<String>に変換するようにgetScheduledを変更し、それを表示する箇所はFutureBuiderにして取り出したList<String>をListViewで表示するようにすればいいのではないでしょうか。
表示の更新は、ボタンを押したらsetState(() {});で行う。

完了した通知がある場合に更新したいのであれば、通知の初期化でResponseCallback関連を設定し、それに来たら画面の更新を行うような実装にすればいい。

あえて書くとしたら以下の様な感じになるのかな。
(ResponseCallbackの処理は入れてないです)

dart

1import 'package:flutter/material.dart'; 2import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 3import 'package:timezone/timezone.dart' as tz; 4 5class AccountPage extends StatefulWidget { 6 const AccountPage({super.key}); 7 8 9 State<AccountPage> createState() => _AccountPageState(); 10} 11 12class _AccountPageState extends State<AccountPage> { 13 late Future<List<String>> future; 14 15 void initState() { 16 future = NotificationsUtils.getScheduled(); 17 super.initState(); 18 } 19 20 21 Widget build(BuildContext context) { 22 return Scaffold( 23 appBar: AppBar( 24 title: const Text("Account page"), 25 ), 26 body: Center( 27 child: Column( 28 children: [ 29 // 今すぐ通知 30 TextButton( 31 onPressed: () { 32 NotificationsUtils.notifyNow(); 33 }, 34 child: const Text('今すぐ通知'), 35 ), 36 37 // 通知をスケジュール 38 TextButton( 39 onPressed: () { 40 NotificationsUtils.scheduleNotifications( 41 DateTime.now().add(const Duration(seconds: 60)), 42 ); 43 future = NotificationsUtils.getScheduled(); 44 setState(() {}); 45 }, 46 child: const Text('通知をスケジュール'), 47 ), 48 49 // スケジュールされた通知をすべてキャンセル 50 TextButton( 51 onPressed: () { 52 NotificationsUtils.cancelNotificationsSchedule(); 53 future = NotificationsUtils.getScheduled(); 54 setState(() {}); 55 }, 56 child: const Text('スケジュールされた通知をすべてキャンセル'), 57 ), 58 // 登録された通知を表示 59 Expanded( 60 child: FutureBuilder<List<String>>( 61 future: future, 62 builder: (context, snapshot) { 63 if (snapshot.hasData) { 64 return ListView.builder( 65 itemCount: snapshot.data!.length, 66 itemBuilder: (context, index) => 67 Text(snapshot.data![index]), 68 ); 69 } else if (snapshot.hasError) { 70 return Text('${snapshot.error!}'); 71 } else { 72 return const CircularProgressIndicator(); 73 } 74 }), 75 ) 76 ], 77 ), 78 ), 79 ); 80 } 81} 82 83class NotificationsUtils { 84 // 今すぐ通知する 85 static Future<void> notifyNow() async { 86 final flnp = FlutterLocalNotificationsPlugin(); 87 flnp.show( 88 0, 89 '手動通知', 90 'あなたがボタンをタップしました', 91 const NotificationDetails( 92 android: AndroidNotificationDetails( 93 '1', 94 '手動通知', 95 importance: Importance.high, 96 priority: Priority.high, 97 ), 98 iOS: DarwinNotificationDetails( 99 presentSound: true, 100 ), 101 ), 102 ); 103 } 104 105 // 通知をスケジュール 106 static Future<void> scheduleNotifications(DateTime dateTime, 107 {DateTimeComponents? dateTimeComponents}) async { 108 // idを動的に生成 109 String id = '${dateTime.hour}${dateTime.minute}'; 110 111 // 日時をTimeZoneを考慮した日時に変換する 112 final scheduleTime = tz.TZDateTime.from(dateTime, tz.local); 113 114 // 通知をスケジュールする 115 final flnp = FlutterLocalNotificationsPlugin(); 116 await flnp.zonedSchedule( 117 int.parse(id), 118 'スケジュール通知', 119 'あなたがスケジュールした時間になりました', 120 scheduleTime, 121 const NotificationDetails( 122 android: AndroidNotificationDetails( 123 '2', 124 'スケジュール通知', 125 importance: Importance.high, 126 priority: Priority.high, 127 ), 128 iOS: DarwinNotificationDetails( 129 presentSound: true, 130 ), 131 ), 132 uiLocalNotificationDateInterpretation: 133 UILocalNotificationDateInterpretation.absoluteTime, 134 androidAllowWhileIdle: true, 135 matchDateTimeComponents: DateTimeComponents.time, 136 ); 137 } 138 139 // 登録されている通知の確認 140 static Future<List<String>> getScheduled() async { 141 final flnp = FlutterLocalNotificationsPlugin(); 142 final List<PendingNotificationRequest> pendingNotificationRequests = 143 await flnp.pendingNotificationRequests(); 144 List<String> lists = []; 145 for (var pendingNotificationRequest in pendingNotificationRequests) { 146 lists.add( 147 '予約済みの通知: [id: ${pendingNotificationRequest.id}, title: ${pendingNotificationRequest.title}, body: ${pendingNotificationRequest.body}, payload: ${pendingNotificationRequest.payload}]'); 148 } 149 return lists; 150 } 151 152 // 通知をキャンセル 153 static Future<void> cancelNotificationsSchedule() async { 154 final flnp = FlutterLocalNotificationsPlugin(); 155 await flnp.cancelAll(); 156 } 157}

投稿2023/01/02 00:23

ta.fu

総合スコア1667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asapan

2023/01/02 06:50

期待通りの動作となりました、ありがとうございます!! 記載いただいたコードに対する質問なのですが、listviewで表示する時に`Expanded`を使われていますが、これを使わないと表示が上手くされませんでした。なぜこれを使う必要があるのでしょうか?
asapan

2023/01/02 06:59

助かりました、ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問