Adicione um lembrete ao seu aplicativo com notificações locais flutuantes

Tempo de leitura: 6 minutes

Se você deseja adicionar um lembrete ao seu aplicativo de anotações ou a qualquer outro aplicativo, este artigo o guiará de maneira simples. Eu gostaria de criar uma demonstração simples e fácil de aplicativo de anotações, para ajudá-lo a agendar uma anotação usando 2 pacotes e plug-ins interessantes do flutter.

flutter_local_notifications: ^13.0.0
datetime_picker_formfield_new: ^2.1.0

datetime_picker_formfield me ajudou a selecionar uma data para minha pequena nota. Assim, eu poderia agendar um lembrete de nota usando o plugin flutter_local_notifications. Como a versão que estou usando exigia o agendamento de notificações de acordo com um fuso horário específico e o método de agendamento existente agora está obsoleto, também tive que usar o pacote de fuso horário. Mas você não precisa adicionar isso ao arquivo yaml porque o plugin flutter_local_notifications já usa isso.

 

iOS setup

Para o Swift, adicione este código ao delegado do seu aplicativo.

if #available(iOS 10.0, *) {
  UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}

Para Objective-C

if (@available(iOS 10.0, *)) {
  [UNUserNotificationCenter currentNotificationCenter].delegate = (id<UNUserNotificationCenterDelegate>) self;
}

Antes de fazer uma compilação de versão do Android, consulte a parte de configuração da versão de versão do plugin de notificação leia-me.

Eu criei o widget Stateless para chamar dentro do nosso runApp, outra classe como minha tela de notas que inclui 2 notas. A classe NoteThumbnail pode ser reutilizada para criar notas quantas vezes você quiser. Não usei uma lista longa com notas. Acabei de adicionar duas notas para simplificar a demonstração.

Dentro da minha miniatura criei um método para selecionar uma data e hora usando seletores de data e hora, onde também posso agendar uma notificação de acordo com a data e hora combinadas.

Você pode ver minha tela de notas sem nenhum código necessário para mostrar uma notificação.

import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  //TODO
  //notification initialization and requesting permission
  runApp(const NoteApp());
}

class NoteApp extends StatelessWidget {
  const NoteApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: "My Note Alarm Demo",
      home: MyNoteScreen(),
    );
  }
}

class MyNoteScreen extends StatefulWidget {
  const MyNoteScreen({Key? key}) : super(key: key);

  @override
  _MyNoteScreenState createState() => _MyNoteScreenState();
}

class _MyNoteScreenState extends State<MyNoteScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(15),
        child: Column(
          children: [
            Row(
              children: const [
                Expanded(
                    child: NoteThumbnail(
                        id: 1,
                        color: Color(0xFFFF9C99),
                        title: "Note one",
                        content: "Lorem ipsum")),
                SizedBox(
                  width: 15,
                ),
                Expanded(
                    child: NoteThumbnail(
                        id: 2,
                        color: Color(0xFF6fefb0),
                        title: "Note two",
                        content: "Lorem ipsum")),
              ],
            )
          ],
        ),
      ),
    );
  }
}

class NoteThumbnail extends StatefulWidget {
  final int id;
  final Color color;
  final String title;
  final String content;

  const NoteThumbnail(
      {Key? key,
      required this.id,
      required this.color,
      required this.title,
      required this.content})
      : super(key: key);

  @override
  _NoteThumbnailState createState() => _NoteThumbnailState();
}

class _NoteThumbnailState extends State<NoteThumbnail> {
  DateTime selectedDate = DateTime.now();
  DateTime fullDate = DateTime.now();

  Future<DateTime> _selectDate(BuildContext context) async {
    final date = await showDatePicker(
        context: context,
        firstDate: DateTime(1900),
        initialDate: selectedDate,
        lastDate: DateTime(2100));
    if (date != null) {
      final time = await showTimePicker(
        context: context,
        initialTime: TimeOfDay.fromDateTime(selectedDate),
      );
      if (time != null) {
        setState(() {
          fullDate = DateTimeField.combine(date, time);
        });
        //TODO
        //schedule a notification
      }
      return DateTimeField.combine(date, time);
    } else {
      return selectedDate;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      decoration: BoxDecoration(
        color: widget.color,
        borderRadius: BorderRadius.circular(10.0),
      ),
      padding: const EdgeInsets.all(10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            widget.title,
            style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
          ),
          const SizedBox(
            height: 5,
          ),
          Text(widget.content),
          const SizedBox(
            height: 80,
          ),
          Text(fullDate.toString()),
          const SizedBox(
            height: 15,
          ),
          ElevatedButton(
              onPressed: () => _selectDate(context),
              child: const Text("Add reminder"))
        ],
      ),
    );
  }
}

Antes de concluir as tarefas TODO na tela de notas, precisamos criar nosso manipulador de notificações. É melhor ter uma única classe de serviço para lidar com todo o trabalho que esperamos do plug-in de notificação de vibração.

Tarefas que eu esperava do manipulador de notificação

  1. Inicialize uma instância de plug-in com configurações de notificação do Android e iOS
  2. Inicializar os fusos horários
  3. Criar ação de notificação selecionada (Ex: — navegar para uma página específica)
  4. Solicitar permissão do iOS
  5. Neste caso agende uma notificação

Além desses pontos principais, também adicionei o método para mostrar as notificações.

Primeiro você precisa criar uma classe de serviço de notificação e uma instância do plug-in de notificação.

class NotificationService {
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
  FlutterLocalNotificationsPlugin();
}

Em seguida, 1 e 2 da lista de tarefas acima.

Future<void> init() async {

  //Initialization Settings for Android
  const AndroidInitializationSettings initializationSettingsAndroid =
  AndroidInitializationSettings('@mipmap/ic_launcher');

  //Initialization Settings for iOS
  const IOSInitializationSettings initializationSettingsIOS =
  IOSInitializationSettings(
    requestSoundPermission: false,
    requestBadgePermission: false,
    requestAlertPermission: false,
  );

  //Initializing settings for both platforms (Android & iOS)
  const InitializationSettings initializationSettings =
  InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS);

  tz.initializeTimeZones();

  await flutterLocalNotificationsPlugin.initialize(
      initializationSettings,
      onSelectNotification: onSelectNotification
  );
}

Você deve verificar suas configurações e o nome do ícone de acordo. Certifique-se de inicializar os fusos horários dentro do método init também. Para o terceiro ponto, acabei de adicionar um método vazio para que você possa adicionar a navegação a uma tela desejada.

onSelectNotification(String? payload) async {
  //Navigate to wherever you want
}

Então você tem que adicionar um método para solicitar permissão do iOS.

requestIOSPermissions() {
  flutterLocalNotificationsPlugin
      .resolvePlatformSpecificImplementation<
      IOSFlutterLocalNotificationsPlugin>()
      ?.requestPermissions(
    alert: true,
    badge: true,
    sound: true,
  );
}

Em seguida, crie a função de notificação de agendamento de acordo com a versão do plug-in que você está usando.

Future<void> scheduleNotifications({id, title, body, time}) async {
   tentar{
     aguarde flutterLocalNotificationsPlugin.zonedSchedule(
         eu ia,
         título,
         corpo,
         tz.TZDateTime.from(hora, tz.local),
         const NotificationDetails(
             android: AndroidNotificationDetails(
                 'id do seu canal', 'nome do seu canal',
                 channelDescription: 'a descrição do seu canal')),
         androidAllowWhileIdle: verdadeiro,
         uiLocalNotificationDateInterpretação:
         UILocalNotificationDateInterpretation.absoluteTime);
   }pegar(e){
     imprimir(e);
   }
}

Aqui eu dei os valores para id, title, body e date time do método select date. Eu adicionei isso dentro de um try catch porque ele dará um erro se você não selecionar uma data futura. Acabei de imprimir o erro, mas certifique-se de adicionar uma mensagem de brinde ou dar qualquer feedback antes de usar isso em um aplicativo.

Você precisa importar os seguintes para usar as funções de fuso horário.

import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

Mesmo que este plug-in de fuso horário não possa fornecer o fuso horário do dispositivo, ele ajudará você a agendar uma notificação de acordo com um fuso horário específico. De acordo com o plug-in leia-me, os desenvolvedores precisam usar algum outro plug-in ou canais de método para obter o fuso horário atual em um dispositivo.

Além das tarefas acima, adicionarei o método de notificação show da seguinte forma.

Future<void> showNotifications({id, title, body, payload}) async {
  const AndroidNotificationDetails androidPlatformChannelSpecifics =
  AndroidNotificationDetails('your channel id', 'your channel name',
      channelDescription: 'your channel description',
      importance: Importance.max,
      priority: Priority.high,
      ticker: 'ticker');
  const NotificationDetails platformChannelSpecifics =
  NotificationDetails(android: androidPlatformChannelSpecifics);
  await flutterLocalNotificationsPlugin.show(
  id, title, body, platformChannelSpecifics,
      payload: payload);
}

Quando você juntar toda a classe, ficará assim.

 

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;

class NotificationService {

  //instance of FlutterLocalNotificationsPlugin
  final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
  FlutterLocalNotificationsPlugin();

  Future<void> init() async {

    //Initialization Settings for Android
    const AndroidInitializationSettings initializationSettingsAndroid =
    AndroidInitializationSettings('@mipmap/ic_launcher');

    //Initialization Settings for iOS
    const IOSInitializationSettings initializationSettingsIOS =
    IOSInitializationSettings(
      requestSoundPermission: false,
      requestBadgePermission: false,
      requestAlertPermission: false,
    );

    //Initializing settings for both platforms (Android & iOS)
    const InitializationSettings initializationSettings =
    InitializationSettings(
        android: initializationSettingsAndroid,
        iOS: initializationSettingsIOS);

    tz.initializeTimeZones();

    await flutterLocalNotificationsPlugin.initialize(
        initializationSettings,
        onSelectNotification: onSelectNotification
    );
  }

   onSelectNotification(String? payload) async {
    //Navigate to wherever you want
  }


   requestIOSPermissions() {
    flutterLocalNotificationsPlugin
        .resolvePlatformSpecificImplementation<
        IOSFlutterLocalNotificationsPlugin>()
        ?.requestPermissions(
      alert: true,
      badge: true,
      sound: true,
    );
  }


  Future<void> showNotifications({id, title, body, payload}) async {
    const AndroidNotificationDetails androidPlatformChannelSpecifics =
    AndroidNotificationDetails('your channel id', 'your channel name',
        channelDescription: 'your channel description',
        importance: Importance.max,
        priority: Priority.high,
        ticker: 'ticker');
    const NotificationDetails platformChannelSpecifics =
    NotificationDetails(android: androidPlatformChannelSpecifics);
    await flutterLocalNotificationsPlugin.show(
    id, title, body, platformChannelSpecifics,
        payload: payload);
  }


  Future<void> scheduleNotifications({id, title, body, time}) async {
    try{
      await flutterLocalNotificationsPlugin.zonedSchedule(
          id,
          title,
          body,
          tz.TZDateTime.from(time, tz.local),
          const NotificationDetails(
              android: AndroidNotificationDetails(
                  'your channel id', 'your channel name',
                  channelDescription: 'your channel description')),
          androidAllowWhileIdle: true,
          uiLocalNotificationDateInterpretation:
          UILocalNotificationDateInterpretation.absoluteTime);
    }catch(e){
      print(e);
    }
  }
}

Agora você pode importar esta aula para sua tela de anotações e concluir as tarefas TODO.

Agora seu método principal ficará assim.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await NotificationService().init(); //
  await NotificationService().requestIOSPermissions(); //
  runApp(const NoteApp());
}

Dentro da classe thumbnail você pode criar a instância do Notification Service.

final NotificationService _notificationService = NotificationService();

Se a data e a hora não forem nulas, você pode agendar a notificação.

await _notificationService.scheduleNotifications(
    id: widget.id,
    title: widget.title,
    body: widget.content,
    time: fullDate);

 

Ótimo!! Agora criamos uma demonstração de nota simples com notificações locais.

Por favor, siga os links para os plugins mais detalhes. Você pode ler coisas mais importantes sobre o plug-in de notificação no readme.