Reputation: 5207
I have an app which uses local notifications that contain some data. In this case it's some quotes read from a JSON file.
What I'm trying to do is to read another quote every minute and update it in main screen.
void main() {
runApp(ChangeNotifierProvider<QuotesRepository>(
create: (context) => QuotesRepository.instance(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
),
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var quote = Provider.of<QuotesRepository>(context);
quote.showScheduledNotification();
return Scaffold(
appBar: new AppBar(
backgroundColor: Colors.red,
title: new Text('Flutter notification demo'),
),
body: new Center(
child: Text(
quote.lastMessage.toString(),
style: GoogleFonts.clickerScript(fontSize: 40.0),
),
),
);
}
}
And this is the place where provider is defined:
class QuotesRepository with ChangeNotifier {
String? lastMessage;
QuotesRepository.instance();
Future<void> loadJson(int index) async {
String data = await rootBundle.loadString('assets/quotes.json');
Map<String, dynamic> userMap = jsonDecode(data);
this.lastMessage = userMap['quotes'][index]['quote'];
}
Future<void> showScheduledNotification() async {
int randomValue = 42;
await loadJson(randomValue).then((value) => showNotificationBody());
notifyListeners();
}
Future<void> showNotificationBody() async {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
print("Function called");
const androidPlatformChannelSpecifics = AndroidNotificationDetails(
'channel id',
'channel name',
'channel description',
icon: 'flutter_devs',
largeIcon: DrawableResourceAndroidBitmap('flutter_devs'),
);
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
const NotificationDetails platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
);
flutterLocalNotificationsPlugin.periodicallyShow(0, '365<3',
this.lastMessage, RepeatInterval.everyMinute, platformChannelSpecifics,
payload: this.lastMessage);
}
}
However this calls the function every second and causes app to crash.
I know it is because I call quote.showScheduledNotification();
and notifyListeners()
will cause that it goes inside endless loop.
But how to prevent this? How to load it only once and then update lastMessage
each time when notification arrives.
For the sake of simplicity I am showing the same quote every time (42).
Upvotes: 0
Views: 925
Reputation: 483
Seems like you are looking for an init functionality. If you transform MyApp
into a stateful widget, you will be able to use the init method of the stateful
widget. This should solve one part of the problem. You should almost never have init functionality inside a build
function, since it will be called quite often.
class MyApp extends StatefulWidget {
const MyApp();
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
var quote = Provider.of<QuotesRepository>(context);
quote.showScheduledNotification();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
backgroundColor: Colors.red,
title: new Text('Flutter notification demo'),
),
body: new Center(
child: Text(
Provider.of<QuotesRepository>(context).lastMessage.toString(),
style: GoogleFonts.clickerScript(fontSize: 40.0),
),
),
);
}
}
PS: For more tips on how to improve the performance of your app, I wrote an article on my blog.
Upvotes: 1