Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert DateTime to TZDateTime in flutter?

I am trying to push local notification through my app by using flutter_local_notifications package and as on FlutterNotificationsPlugin the ".schedule" is deprecated I tried using ".zonedSchedule" but it requires TZDateTime value.

var androidDetails = AndroidNotificationDetails(t.id, "Task Notifier",
        "Notifies if you have a task scheduled at a particular time");
    var generalNotificationDetails =
        NotificationDetails(android: androidDetails);
    var now = DateTime.now();
    var scheduledTime = DateTime(
            now.year, now.month, _dayTasks.date.day, t.time.hour, t.time.minute)
        .subtract(Duration(minutes: 5));
    //use zonedSchedule once you figure out how to convert datetime to TZdatetime
    // await notifications.zonedSchedule(
    //     0, "Task", t.description, scheduledTime, generalNotificationDetails,
    //     androidAllowWhileIdle: true,
    //     uiLocalNotificationDateInterpretation:
    //         UILocalNotificationDateInterpretation.wallClockTime);
    await notifications.schedule(0, "Ideal Day Task", t.description,
        scheduledTime, generalNotificationDetails);
like image 533
Satyam Pandey Avatar asked Oct 11 '20 14:10

Satyam Pandey


2 Answers

Indeed, zonedSchedule() requires the TZDateTime value. The approach I took was to utilize another package to convert the original DateTime value to a TZDateTime value. Below is an excerpt of one implementation I've used. See the fifth line? It involves the function, TZDateTime.from(). It takes the DateTime you may have originally used and converts it to TZDateTime. However, it needs your current location. That's where the class, TimeZone, comes in.

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

    final timeZone = TimeZone();

    // The device's timezone.
    String timeZoneName = await timeZone.getTimeZoneName();

    // Find the 'current location'
    final location = await timeZone.getLocation(timeZoneName);

    final scheduledDate = tz.TZDateTime.from(dateTime, location);

    try {
      await _flutterLocalNotificationsPlugin.zonedSchedule(
        id,
        title,
        body,
        scheduledDate,
        platformChannelSpecifics,
        androidAllowWhileIdle: androidAllowWhileIdle,
        payload: payload,
        uiLocalNotificationDateInterpretation:
            uiLocalNotificationDateInterpretation,
      );
    } catch (ex) {
      id = -1;
    }
    return id;

I quickly wrote the class, TimeZone. This class works with yet another plugin (flutter_native_timezone) that looks for the 'current location' of the device and provides the standard name for that locale:

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

class TimeZone {
  factory TimeZone() => _this ?? TimeZone._();

  TimeZone._() {
    initializeTimeZones();
  }
  static TimeZone _this;

  Future<String> getTimeZoneName() async => FlutterNativeTimezone.getLocalTimezone();

  Future<t.Location> getLocation([String timeZoneName]) async {
    if(timeZoneName == null || timeZoneName.isEmpty){
      timeZoneName = await getTimeZoneName();
    }
    return t.getLocation(timeZoneName);
  }
}

Use that class, and you're good to go.

like image 158
Andrious Solutions Avatar answered Oct 19 '22 13:10

Andrious Solutions


I had just tried to solve the same problem. To understand this, we will need to have a basic understanding of platform channel.

https://flutter.dev/docs/development/platform-integration/platform-channels?tab=android-channel-kotlin-tab

Once we understand that, the code in the sample should become more understandable: https://github.com/MaikuB/flutter_local_notifications/blob/1fe78b9d129d674cd97cfba49734577b24c56925/flutter_local_notifications/example/android/app/src/main/java/com/dexterous/flutterlocalnotificationsexample/MainActivity.java

Concept in action:

First of all, we will need to import tz like so. This can be done in your main.dart or a helper file.

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

To create a TZDateTime from a plain dateTime, we will need to use the device timezone through queried through the platform channel.

Future<void> configureLocalTimeZone() async {
  tz.initializeTimeZones();
  final String timeZoneName = await platform.invokeMethod('getTimeZoneName');
  tz.setLocalLocation(tz.getLocation(timeZoneName));
}

Where getTimeZoneName is implemented in the platform specific implementation.

Be sure to call configureLocalTimeZone before the next step.

And when we want to create the TZDateTime from a DateTime object, we can usually just do like so

var time = tz.TZDateTime.from(
    scheduledNotificationDateTime,
    tz.local,
);

The full implementation is showcased in the example code here: https://github.com/MaikuB/flutter_local_notifications/blob/1fe78b9d129d674cd97cfba49734577b24c56925/flutter_local_notifications/example/lib/main.dart

I have not considered the different edge cases in my very simple work flow so YMMV. Hope this helps! :)

like image 7
iamdavidlam Avatar answered Oct 19 '22 13:10

iamdavidlam