Repository: builttoroam/device_calendar

Issue:
Json methods use different attributes
The `event` model uses attribute names with a different name in `toJson` and `fromJson`. #316 covers the start and end time fields, but `title`, `description`, `allDay`, and `url` also have this issue.

Example:

```dart
Map<String, dynamic> toJson() {
  ...
  data['eventTitle'] = title;
  data['eventDescription'] = description;
```

```dart
Event.fromJson(Map<String, dynamic>? json) {
    ...
    title = json['title'];
    description = json['description'];
```

There is currently a unit test for some of these fields in `Event_Serialises_Correctly`, which fails.

I'd be happy to submit a PR to fix the attribute changes.

I would also suggest having the CI run the `FlutterTest@0` task as a check - and can include that in the same PR if the owners agree it should be included.



Fix patch:
diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml
index 2df8f3f8..76e458f5 100644
--- a/.github/workflows/dart.yml
+++ b/.github/workflows/dart.yml
@@ -16,6 +16,23 @@ on:
 jobs:
   test:
     name: Build test
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - uses: subosito/flutter-action@v1
+        with:
+          channel: "stable"
+      - run: dart --version
+      - run: flutter --version
+      - run: flutter test
+      - name: publish test
+        run: flutter pub publish --dry-run
+      - name: android build
+        run: |
+          cd example
+          flutter build appbundle
+  test-macos:
+    name: iOS build test
     runs-on: macos-latest
     steps:
       - uses: actions/checkout@v2
@@ -27,12 +44,7 @@ jobs:
           channel: "stable"
       - run: dart --version
       - run: flutter --version
-      - run: flutter test
       - name: iOS build
         run: |
           cd example
-          flutter build ios --release --no-codesign
-      - name: android build
-        run: |
-          cd example
-          flutter build appbundle
+          flutter build ios --release --no-codesign
\ No newline at end of file
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index 48cb8c38..b836df6a 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -36,14 +36,15 @@ jobs:
         run: |
           sed -i "0,/\#\# \[.*/s//## [${{steps.changelog_reader.outputs.version}}-$GITHUB_RUN_ID]/" CHANGELOG.md
           cat CHANGELOG.md
+      - name: Setup credentials
+        run: |
+          cat <<EOF > $PUB_CACHE/credentials.json
+          ${{ secrets.CREDENTIALS }}
+          EOF
       - name: Publish package
-        uses: sakebook/actions-flutter-pub-publisher@v1.3.1
-        with:
-          credential: ${{ secrets.CREDENTIALS }}
-          flutter_package: true
-          skip_test: false
+        run: flutter pub publish --force
       - name: Add entry to Github release
         uses: softprops/action-gh-release@v1
         with:
           tag_name: ${{ steps.changelog_reader.outputs.version }}+${{ github.run_id }}
-          prerelease: true
+          prerelease: true
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c5e2eb0f..35967b42 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -25,12 +25,13 @@ jobs:
           channel: "stable"
       - run: dart --version
       - run: flutter --version
+      - name: Setup credentials
+        run: |
+          cat <<EOF > $PUB_CACHE/credentials.json
+          ${{ secrets.CREDENTIALS }}
+          EOF
       - name: Publish package
-        uses: sakebook/actions-flutter-pub-publisher@v1.3.1
-        with:
-          credential:  ${{ secrets.CREDENTIALS }}
-          flutter_package: true
-          skip_test: false
+        run: flutter pub publish --force
       - name: Get Changelog Entry
         id: changelog_reader
         uses: mindsers/changelog-reader-action@v2.0.0
@@ -38,4 +39,4 @@ jobs:
         uses: softprops/action-gh-release@v1
         with:
           tag_name: ${{ steps.changelog_reader.outputs.version }}
-          body: ${{ steps.changelog_reader.outputs.changes }}
+          body: ${{ steps.changelog_reader.outputs.changes }}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 801b1942..69ba4021 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,26 +1,37 @@
 # Changelog
 
-To benefit from the current changelog reader in CI/CD, please follow the changelog format from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+<!-- To benefit from the current changelog reader in CI/CD, please follow the changelog format from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -->
 
-## [4.0.1]
+## [4.1.0](https://github.com/builttoroam/device_calendar/releases/tag/4.1.0)
 
-- Timezone plugin and logic implemented. Fixed all issues related to timezone.
+- Fix: Event JSONs created are now readable.
+- Fix: Compilable for Flutter 2.9+
+- Fix: removed depreceated plugins from Example.
+- Fix: Integration tests are now working. Android instructions are ready.
+- Gradle plug-ins are updated.
+
+## [4.0.1](https://github.com/builttoroam/device_calendar/releases/tag/4.0.1)
+
+- Fixes event time retrieved
+
+## [4.0.0](https://github.com/builttoroam/device_calendar/releases/tag/4.0.0)
+
+- Timezone plugin and logic implemented. All issues related to timezone shoulde be fixed.
+- Events.availability defaults to busy when not specified [354](https://github.com/builttoroam/device_calendar/pull/354)
+- Events parameter now includes location and url. [319](https://github.com/builttoroam/device_calendar/pull/319)
 - Android: Fixed bug where platform exception appeared, when Events.availability was null on Event [241](https://github.com/builttoroam/device_calendar/issues/241)
-- Fixed example app issues [270](https://github.com/builttoroam/device_calendar/issues/270), [268](https://github.com/builttoroam/device_calendar/issues/268)
-- Android code variable order fix for issue [258](https://github.com/builttoroam/device_calendar/issues/258)
+- Fixed various issues in example [270](https://github.com/builttoroam/device_calendar/issues/270), [268](https://github.com/builttoroam/device_calendar/issues/268)
+- Android: deleteEvent code aligned with flutter [258](https://github.com/builttoroam/device_calendar/issues/258)
+- Android: Updated to V2 embeddding [326](https://github.com/builttoroam/device_calendar/issues/326)
+- iOS: Updated swift versions, possibly improved compability with Obj-C [flutter/flutter#16049 (comment)](https://github.com/flutter/flutter/issues/16049#issuecomment-611192738)
 
-## [4.0.0]
+## [3.9.0](https://github.com/builttoroam/device_calendar/releases/tag/3.9.0)
 
 - Migrated to null safety
 - Updated multiple underlying dependencies
 - Rebuilt iOS podfile
 - Upgraded to new Android plugins APIs for flutter
 
-## 3.2.0
-
-- Added time zone support
-- Project hierarchy update and clean up
-
 ## 3.1.0 25th March 2020 - Bug fixes and new features
 
 - Boolean variable `isDefault` added for issue [145](https://github.com/builttoroam/device_calendar/issues/145) (**NOTE**: This is not supported Android API 16 or lower, `isDefault` will always be false)
diff --git a/README.md b/README.md
index de8f7e7a..bdb4d994 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,9 @@
 # Device Calendar Plugin
-**Your previous code will need to be modified (slightly) otherwise it will not run after update. See Timezone support for more details.**
 
-[![pub package](https://img.shields.io/pub/v/device_calendar.svg)](https://pub.dartlang.org/packages/device_calendar) [![Build Status](https://dev.azure.com/builttoroam/Flutter%20Plugins/_apis/build/status/Device%20Calendar)](https://dev.azure.com/builttoroam/Flutter%20Plugins/_build/latest?definitionId=111)
+**If you're upgrading to v4, your previous code will need to be modified (slightly) otherwise it will not run after update. See [Timezone support](https://github.com/builttoroam/device_calendar#timezone-support-with-tzdatetime) for more details.**
+**There are some changes to event JSON formats at v4. Pay extra care if handld event JSONs. Directly calling to and from device calendars should be unaffected.**
+
+[![pub package](https://img.shields.io/pub/v/device_calendar.svg)](https://pub.dartlang.org/packages/device_calendar) ![Pub Version (including pre-releases)](https://img.shields.io/pub/v/device_calendar?include_prereleases&label=Prerelease) [![build](https://github.com/builttoroam/device_calendar/actions/workflows/dart.yml/badge.svg?branch=develop)](https://github.com/builttoroam/device_calendar/actions/workflows/dart.yml)
 
 A cross platform plugin for modifying calendars on the user's device.
 
@@ -23,7 +25,7 @@ A cross platform plugin for modifying calendars on the user's device.
 
 ## Timezone support with TZDateTime
 
-Due to feedback we received, we will be utilizing `timezone` package to better handle all the timezone data. 
+Due to feedback we received, starting from `4.0.0` we will be using the `timezone` package to better handle all timezone data.
 
 This is already included in this package. However, you need to add this line whenever the package is needed.
 
@@ -33,10 +35,10 @@ import 'package:timezone/timezone.dart';
 
 If you don't need any timezone specific features in your app, you may use `flutter_native_timezone` to get your devices' current timezone, then convert your previous `DateTime` with it.
 
-```dart 
+```dart
 import 'package:flutter_native_timezone/flutter_native_timezone.dart';
 
-//As an example, our default timezone is UTC.
+// As an example, our default timezone is UTC.
 Location _currentLocation = getLocation('Etc/UTC');
 
 Future setCurentLocation() async {
@@ -57,25 +59,27 @@ event.start = TZDateTime.from(oldDateTime, _currentLocation);
 
 For other use cases, feedback or future developments on the feature, feel free to open a discussion on GitHub.
 
-## Null migration
+## Null-safety migration
 
-From v4.0.0, device_calendar fits null safety. However, not all workflow had been checked and bugs from 3.2 still presists.
+From `v3.9.0`, device_calendar is null-safe. However, not all workflows have been checked and bugs from older versions still persist.
 
-You are strongly advised to test your workflow with the new package before shipping. 
+You are strongly advised to test your workflow with the new package before shipping.
 Better yet, please leave a note for what works and what doesn't, or contribute some bug fixes!
 
 ## Android Integration
 
-The following will need to be added to the manifest file for your application to indicate permissions to modify calendars a needed
+The following will need to be added to the `AndroidManifest.xml` file for your application to indicate permissions to modify calendars are needed
 
 ```xml
 <uses-permission android:name="android.permission.READ_CALENDAR" />
 <uses-permission android:name="android.permission.WRITE_CALENDAR" />
 ```
+
 ### Proguard / R8 exceptions
+
 By default, all android apps go through R8 for file shrinking when building a release version. Currently, it interferes with some functions such as `retrieveCalendars()`.
 
-You may add the following setting to the ProGuard rules file (thanks to [Britannio Jarrett](https://github.com/britannio)). Read more about the issue [here](https://github.com/builttoroam/device_calendar/issues/99)
+You may add the following setting to the ProGuard rules file `proguard-rules.pro` (thanks to [Britannio Jarrett](https://github.com/britannio)). Read more about the issue [here](https://github.com/builttoroam/device_calendar/issues/99)
 
 ```
 -keep class com.builttoroam.devicecalendar.** { *; }
@@ -86,11 +90,12 @@ See [here](https://github.com/builttoroam/device_calendar/issues/99#issuecomment
 For more information, refer to the guide at [Android Developer](https://developer.android.com/studio/build/shrink-code#keep-code)
 
 ### AndroidX migration
-**IMPORTANT**: Since version 0.1.0, this version has migrated to use AndroidX instead of the deprecated Android support libraries. When using version 0.10.0 and onwards for this plugin, please ensure your application has been migrated following the guide [here](https://developer.android.com/jetpack/androidx/migrate)
+
+Since `v.1.0`, this version has migrated to use AndroidX instead of the deprecated Android support libraries. When using `0.10.0` and onwards for this plugin, please ensure your application has been migrated following the guide [here](https://developer.android.com/jetpack/androidx/migrate)
 
 ## iOS Integration
 
-For iOS 10 support, you'll need to modify the Info.plist to add the following key/value pair
+For iOS 10 support, you'll need to modify the `Info.plist` to add the following key/value pair
 
 ```xml
 <key>NSCalendarsUsageDescription</key>
diff --git a/android/build.gradle b/android/build.gradle
index 4f4f3a6c..84250c59 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -2,14 +2,14 @@ group 'com.builttoroam.devicecalendar'
 version '1.0-SNAPSHOT'
 
 buildscript {
-    ext.kotlin_version = '1.3.50'
+    ext.kotlin_version = '1.6.0'
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.1.0'
+        classpath 'com.android.tools.build:gradle:4.1.3'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
@@ -17,7 +17,7 @@ buildscript {
 rootProject.allprojects {
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
 }
 
@@ -37,12 +37,20 @@ android {
     lintOptions {
         disable 'InvalidPackage'
     }
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_1_8.toString()
+    }
 }
 
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    implementation 'com.google.code.gson:gson:2.8.5'
-    api 'androidx.appcompat:appcompat:1.0.2'
+    implementation 'com.google.code.gson:gson:2.8.8'
+    api 'androidx.appcompat:appcompat:1.4.0'
     implementation 'org.dmfs:lib-recur:0.11.2'
-    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
 }
diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt
index e3dc8b24..0be60d73 100644
--- a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt
+++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt
@@ -1,4 +1,4 @@
-package com.builttoroam.devicecalendar 
+package com.builttoroam.devicecalendar
 
 import android.Manifest
 import android.annotation.SuppressLint
@@ -467,11 +467,11 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
     private fun buildEventContentValues(event: Event, calendarId: String): ContentValues {
         val values = ContentValues()
         val duration: String? = null
-        values.put(Events.ALL_DAY, event.allDay)
+        values.put(Events.ALL_DAY, event.eventAllDay)
 
-        if (event.allDay) {
+        if (event.eventAllDay) {
             val calendar = java.util.Calendar.getInstance()
-            calendar.timeInMillis = event.start!!
+            calendar.timeInMillis = event.eventStartDate!!
             calendar.set(java.util.Calendar.HOUR, 0)
             calendar.set(java.util.Calendar.MINUTE, 0)
             calendar.set(java.util.Calendar.SECOND, 0)
@@ -479,18 +479,18 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
 
             values.put(Events.DTSTART, calendar.timeInMillis)
             values.put(Events.DTEND, calendar.timeInMillis)
-            values.put(Events.EVENT_TIMEZONE, getTimeZone(event.startTimeZone).id)
+            values.put(Events.EVENT_TIMEZONE, getTimeZone(event.eventStartTimeZone).id)
         } else {
-            values.put(Events.DTSTART, event.start!!)
-            values.put(Events.EVENT_TIMEZONE, getTimeZone(event.startTimeZone).id)
+            values.put(Events.DTSTART, event.eventStartDate!!)
+            values.put(Events.EVENT_TIMEZONE, getTimeZone(event.eventStartTimeZone).id)
 
-            values.put(Events.DTEND, event.end!!)
-            values.put(Events.EVENT_END_TIMEZONE, getTimeZone(event.endTimeZone).id)
+            values.put(Events.DTEND, event.eventEndDate!!)
+            values.put(Events.EVENT_END_TIMEZONE, getTimeZone(event.eventEndTimeZone).id)
         }
-        values.put(Events.TITLE, event.title)
-        values.put(Events.DESCRIPTION, event.description)
-        values.put(Events.EVENT_LOCATION, event.location)
-        values.put(Events.CUSTOM_APP_URI, event.url)
+        values.put(Events.TITLE, event.eventTitle)
+        values.put(Events.DESCRIPTION, event.eventDescription)
+        values.put(Events.EVENT_LOCATION, event.eventLocation)
+        values.put(Events.CUSTOM_APP_URI, event.eventURL)
         values.put(Events.CALENDAR_ID, calendarId)
         values.put(Events.DURATION, duration)
         values.put(Events.AVAILABILITY, getAvailability(event.availability))
@@ -713,18 +713,18 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
         val availability = parseAvailability(cursor.getInt(EVENT_PROJECTION_AVAILABILITY_INDEX))
 
         val event = Event()
-        event.title = title ?: "New Event"
+        event.eventTitle = title ?: "New Event"
         event.eventId = eventId.toString()
         event.calendarId = calendarId
-        event.description = description
-        event.start = begin
-        event.end = end
-        event.allDay = allDay
-        event.location = location
-        event.url = url
+        event.eventDescription = description
+        event.eventStartDate = begin
+        event.eventEndDate = end
+        event.eventAllDay = allDay
+        event.eventLocation = location
+        event.eventURL = url
         event.recurrenceRule = parseRecurrenceRuleString(recurringRule)
-        event.startTimeZone = startTimeZone
-        event.endTimeZone = endTimeZone
+        event.eventStartTimeZone = startTimeZone
+        event.eventEndTimeZone = endTimeZone
         event.availability = availability
 
         return event
@@ -863,7 +863,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
     @Synchronized
     private fun generateUniqueRequestCodeAndCacheParameters(parameters: CalendarMethodsParametersCacheModel): Int {
         // TODO we can ran out of Int's at some point so this probably should re-use some of the freed ones
-        val uniqueRequestCode: Int = (_cachedParametersMap.keys.max() ?: 0) + 1
+        val uniqueRequestCode: Int = (_cachedParametersMap.keys.maxOrNull() ?: 0) + 1
         parameters.ownCacheKey = uniqueRequestCode
         _cachedParametersMap[uniqueRequestCode] = parameters
 
diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt
index a2cdba83..34b8c222 100644
--- a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt
+++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt
@@ -164,17 +164,17 @@ class DeviceCalendarPlugin() : FlutterPlugin, MethodCallHandler, ActivityAware {
 
     private fun parseEventArgs(call: MethodCall, calendarId: String?): Event {
         val event = Event()
-        event.title = call.argument<String>(EVENT_TITLE_ARGUMENT)
+        event.eventTitle = call.argument<String>(EVENT_TITLE_ARGUMENT)
         event.calendarId = calendarId
         event.eventId = call.argument<String>(EVENT_ID_ARGUMENT)
-        event.description = call.argument<String>(EVENT_DESCRIPTION_ARGUMENT)
-        event.allDay = call.argument<Boolean>(EVENT_ALL_DAY_ARGUMENT) ?: false
-        event.start = call.argument<Long>(EVENT_START_DATE_ARGUMENT)!!
-        event.end = call.argument<Long>(EVENT_END_DATE_ARGUMENT)!!
-        event.startTimeZone = call.argument<String>(EVENT_START_TIMEZONE_ARGUMENT)
-        event.endTimeZone = call.argument<String>(EVENT_END_TIMEZONE_ARGUMENT)
-        event.location = call.argument<String>(EVENT_LOCATION_ARGUMENT)
-        event.url = call.argument<String>(EVENT_URL_ARGUMENT)
+        event.eventDescription = call.argument<String>(EVENT_DESCRIPTION_ARGUMENT)
+        event.eventAllDay = call.argument<Boolean>(EVENT_ALL_DAY_ARGUMENT) ?: false
+        event.eventStartDate = call.argument<Long>(EVENT_START_DATE_ARGUMENT)!!
+        event.eventEndDate = call.argument<Long>(EVENT_END_DATE_ARGUMENT)!!
+        event.eventStartTimeZone = call.argument<String>(EVENT_START_TIMEZONE_ARGUMENT)
+        event.eventEndTimeZone = call.argument<String>(EVENT_END_TIMEZONE_ARGUMENT)
+        event.eventLocation = call.argument<String>(EVENT_LOCATION_ARGUMENT)
+        event.eventURL = call.argument<String>(EVENT_URL_ARGUMENT)
         event.availability = parseAvailability(call.argument<String>(EVENT_AVAILABILITY_ARGUMENT))
 
         if (call.hasArgument(RECURRENCE_RULE_ARGUMENT) && call.argument<Map<String, Any>>(RECURRENCE_RULE_ARGUMENT) != null) {
diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt
index 2b9242af..5fc8a774 100644
--- a/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt
+++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt
@@ -2,17 +2,17 @@ package com.builttoroam.devicecalendar.models
 
 
 class Event {
-    var title: String? = null
+    var eventTitle: String? = null
     var eventId: String? = null
     var calendarId: String? = null
-    var description: String? = null
-    var start: Long? = null
-    var end: Long? = null
-    var startTimeZone: String? = null
-    var endTimeZone: String? = null
-    var allDay: Boolean = false
-    var location: String? = null
-    var url: String? = null
+    var eventDescription: String? = null
+    var eventStartDate: Long? = null
+    var eventEndDate: Long? = null
+    var eventStartTimeZone: String? = null
+    var eventEndTimeZone: String? = null
+    var eventAllDay: Boolean = false
+    var eventLocation: String? = null
+    var eventURL: String? = null
     var attendees: MutableList<Attendee> = mutableListOf()
     var recurrenceRule: RecurrenceRule? = null
     var organizer: Attendee? = null
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 9179e557..9d6a815c 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -16,7 +16,7 @@ apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 android {
-    compileSdkVersion 29
+    compileSdkVersion 31
     ndkVersion '22.1.7171670'
 
     sourceSets {
@@ -31,7 +31,7 @@ android {
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.builttoroam.devicecalendarexample"
         minSdkVersion 16
-        targetSdkVersion 29
+        targetSdkVersion 31
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -52,7 +52,7 @@ flutter {
 
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'androidx.test:runner:1.2.0'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test:runner:1.4.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 }
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index b66dec90..630265e9 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -22,6 +22,7 @@
     android:value="2" />
         <activity
             android:name=".MainActivity"
+            android:exported="true"
             android:launchMode="singleTop"
             android:theme="@style/LaunchTheme"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 2e8731dd..c85ba2ad 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,12 +1,12 @@
 buildscript {
-    ext.kotlin_version = '1.3.41'
+    ext.kotlin_version = '1.6.0'
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.0.0'
+        classpath 'com.android.tools.build:gradle:7.0.3'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
@@ -14,15 +14,13 @@ buildscript {
 allprojects {
     repositories {
         google()
-        jcenter()
+        mavenCentral()
     }
 }
 
 rootProject.buildDir = '../build'
 subprojects {
     project.buildDir = "${rootProject.buildDir}/${project.name}"
-}
-subprojects {
     project.evaluationDependsOn(':app')
 }
 
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index b6e61b62..946d709d 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,4 +1,3 @@
 android.enableJetifier=true
 android.useAndroidX=true
-org.gradle.jvmargs=-Xmx1536M
-android.enableR8=true
+org.gradle.jvmargs=-Xmx1536M
\ No newline at end of file
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index f73023f2..6f5b3ec5 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
diff --git a/example/lib/presentation/pages/calendar_add.dart b/example/lib/presentation/pages/calendar_add.dart
index 519928eb..abce3788 100644
--- a/example/lib/presentation/pages/calendar_add.dart
+++ b/example/lib/presentation/pages/calendar_add.dart
@@ -15,7 +15,7 @@ class _CalendarAddPageState extends State<CalendarAddPage> {
   final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
   late DeviceCalendarPlugin _deviceCalendarPlugin;
 
-  bool _autovalidate = false;
+  AutovalidateMode _autovalidate = AutovalidateMode.disabled;
   String _calendarName = '';
   ColorChoice? _colorChoice;
   String _localAccountName = '';
@@ -32,7 +32,7 @@ class _CalendarAddPageState extends State<CalendarAddPage> {
         title: Text('Create Calendar'),
       ),
       body: Form(
-        autovalidate: _autovalidate,
+        autovalidateMode: _autovalidate,
         key: _formKey,
         child: Container(
           padding: EdgeInsets.all(10),
@@ -81,7 +81,8 @@ class _CalendarAddPageState extends State<CalendarAddPage> {
         onPressed: () async {
           final form = _formKey.currentState;
           if (form?.validate() == false) {
-            _autovalidate = true; // Start validating on every change.
+            _autovalidate =
+                AutovalidateMode.always; // Start validating on every change.
             showInSnackBar('Please fix the errors in red before submitting.');
           } else {
             form?.save();
@@ -115,7 +116,8 @@ class _CalendarAddPageState extends State<CalendarAddPage> {
   }
 
   void showInSnackBar(String value) {
-    _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(value)));
+    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value)));
+    // _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(value)));
   }
 }
 
diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart
index b57305d3..3461103c 100644
--- a/example/lib/presentation/pages/calendar_event.dart
+++ b/example/lib/presentation/pages/calendar_event.dart
@@ -38,12 +38,12 @@ class _CalendarEventPageState extends State<CalendarEventPage> {
   final RecurringEventDialog? _recurringEventDialog;
 
   TZDateTime? _startDate;
-  late TimeOfDay _startTime;
+  TimeOfDay? _startTime;
 
   TZDateTime? _endDate;
-  late TimeOfDay _endTime;
+  TimeOfDay? _endTime;
 
-  bool _autovalidate = false;
+  AutovalidateMode _autovalidate = AutovalidateMode.disabled;
   DayOfWeekGroup? _dayOfWeekGroup = DayOfWeekGroup.None;
 
   bool _isRecurringEvent = false;
@@ -181,7 +181,7 @@ class _CalendarEventPageState extends State<CalendarEventPage> {
           child: Column(
             children: [
               Form(
-                autovalidate: _autovalidate,
+                autovalidateMode: _autovalidate,
                 key: _formKey,
                 child: Column(
                   children: [
@@ -808,7 +808,8 @@ class _CalendarEventPageState extends State<CalendarEventPage> {
           onPressed: () async {
             final form = _formKey.currentState;
             if (form?.validate() == false) {
-              _autovalidate = true; // Start validating on every change.
+              _autovalidate =
+                  AutovalidateMode.always; // Start validating on every change.
               showInSnackBar('Please fix the errors in red before submitting.');
             } else {
               form?.save();
@@ -1012,6 +1013,6 @@ class _CalendarEventPageState extends State<CalendarEventPage> {
   }
 
   void showInSnackBar(String value) {
-    _scaffoldKey.currentState!.showSnackBar(SnackBar(content: Text(value)));
+    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value)));
   }
 }
diff --git a/example/lib/presentation/pages/calendar_events.dart b/example/lib/presentation/pages/calendar_events.dart
index 2172b965..0cd6f182 100644
--- a/example/lib/presentation/pages/calendar_events.dart
+++ b/example/lib/presentation/pages/calendar_events.dart
@@ -100,7 +100,7 @@ class _CalendarEventsPageState extends State<CalendarEventsPage> {
     if (deleteSucceeded) {
       await _retrieveCalendarEvents();
     } else {
-      _scaffoldstate.currentState!.showSnackBar(SnackBar(
+      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
         content: Text('Oops, we ran into an issue deleting the event'),
         backgroundColor: Colors.red,
         duration: Duration(seconds: 5),
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 7e8fd492..c6eeea43 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -16,7 +16,7 @@ dependencies:
   cupertino_icons:
 
 dev_dependencies:
-  flutter_driver:
+  integration_test:
     sdk: flutter
   flutter_test:
     sdk: flutter
diff --git a/ios/Classes/SwiftDeviceCalendarPlugin.swift b/ios/Classes/SwiftDeviceCalendarPlugin.swift
index d1a33410..fc092c88 100644
--- a/ios/Classes/SwiftDeviceCalendarPlugin.swift
+++ b/ios/Classes/SwiftDeviceCalendarPlugin.swift
@@ -26,15 +26,15 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin {
     struct Event: Codable {
         let eventId: String
         let calendarId: String
-        let title: String
-        let description: String?
-        let start: Int64
-        let end: Int64
-        let startTimeZone: String?
-        let allDay: Bool
+        let eventTitle: String
+        let eventDescription: String?
+        let eventStartDate: Int64
+        let eventEndDate: Int64
+        let eventStartTimeZone: String?
+        let eventAllDay: Bool
         let attendees: [Attendee]
-        let location: String?
-        let url: String?
+        let eventLocation: String?
+        let eventURL: String?
         let recurrenceRule: RecurrenceRule?
         let organizer: Attendee?
         let reminders: [Reminder]
@@ -350,15 +350,15 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin {
         let event = Event(
             eventId: ekEvent.eventIdentifier,
             calendarId: calendarId,
-            title: ekEvent.title ?? "New Event",
-            description: ekEvent.notes,
-            start: Int64(ekEvent.startDate.millisecondsSinceEpoch),
-            end: Int64(ekEvent.endDate.millisecondsSinceEpoch),
-            startTimeZone: ekEvent.timeZone?.identifier,
-            allDay: ekEvent.isAllDay,
+            eventTitle: ekEvent.title ?? "New Event",
+            eventDescription: ekEvent.notes,
+            eventStartDate: Int64(ekEvent.startDate.millisecondsSinceEpoch),
+            eventEndDate: Int64(ekEvent.endDate.millisecondsSinceEpoch),
+            eventStartTimeZone: ekEvent.timeZone?.identifier,
+            eventAllDay: ekEvent.isAllDay,
             attendees: attendees,
-            location: ekEvent.location,
-            url: ekEvent.url?.absoluteString,
+            eventLocation: ekEvent.location,
+            eventURL: ekEvent.url?.absoluteString,
             recurrenceRule: recurrenceRule,
             organizer: convertEkParticipantToAttendee(ekParticipant: ekEvent.organizer),
             reminders: reminders,
diff --git a/lib/src/device_calendar.dart b/lib/src/device_calendar.dart
index 052da7b8..369f6434 100644
--- a/lib/src/device_calendar.dart
+++ b/lib/src/device_calendar.dart
@@ -3,7 +3,6 @@ import 'dart:convert';
 
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
-import 'package:meta/meta.dart';
 import 'package:sprintf/sprintf.dart';
 import 'package:timezone/timezone.dart';
 
diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart
index 6f316a56..9f67f8b9 100644
--- a/lib/src/models/event.dart
+++ b/lib/src/models/event.dart
@@ -1,8 +1,5 @@
 import '../../device_calendar.dart';
-import '../common/calendar_enums.dart';
 import '../common/error_messages.dart';
-import 'attendee.dart';
-import 'recurrence_rule.dart';
 import 'package:timezone/timezone.dart';
 import 'package:collection/collection.dart';
 
@@ -68,11 +65,11 @@ class Event {
 
     eventId = json['eventId'];
     calendarId = json['calendarId'];
-    title = json['title'];
-    description = json['description'];
+    title = json['eventTitle'];
+    description = json['eventDescription'];
 
     final int? startTimestamp = json['eventStartDate'];
-    final String? startLocationName = json['startTimeZone'];
+    final String? startLocationName = json['eventStartTimeZone'];
     var startTimeZone = timeZoneDatabase.locations[startLocationName];
     startTimeZone ??= local;
     start = startTimestamp != null
@@ -80,18 +77,18 @@ class Event {
         : TZDateTime.now(local);
 
     final int? endTimestamp = json['eventEndDate'];
-    final String? endLocationName = json['endTimeZone'];
+    final String? endLocationName = json['eventEndTimeZone'];
     var endLocation = timeZoneDatabase.locations[endLocationName];
     endLocation ??= local;
     end = endTimestamp != null
         ? TZDateTime.fromMillisecondsSinceEpoch(endLocation, endTimestamp)
         : TZDateTime.now(local);
 
-    allDay = json['allDay'] ?? false;
-    location = json['location'];
+    allDay = json['eventAllDay'] ?? false;
+    location = json['eventLocation'];
     availability = parseStringToAvailability(json['availability']);
 
-    var foundUrl = json['url']?.toString();
+    var foundUrl = json['eventURL']?.toString();
     if (foundUrl?.isEmpty ?? true) {
       url = null;
     } else {
diff --git a/pubspec.yaml b/pubspec.yaml
index 2c9a2237..67f8c22c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,12 +1,11 @@
 name: device_calendar
 description: A cross platform plugin for modifying calendars on the user's device.
-version: 4.0.1
+version: 4.1.0
 homepage: https://github.com/builttoroam/device_calendar/tree/master
 
 dependencies:
   flutter:
     sdk: flutter
-  meta: ^1.7.0
   collection: ^1.15.0
   sprintf: ^6.0.0
   timezone: ^0.8.0
@@ -15,7 +14,7 @@ dependencies:
 dev_dependencies:
   flutter_test:
     sdk: flutter
-  lints: ^1.0.1
+  flutter_lints: ^1.0.4
 
 # The following section is specific to Flutter.
 flutter:


Test patch:
diff --git a/example/integration_test/app_test.dart b/example/integration_test/app_test.dart
new file mode 100644
index 00000000..503a8951
--- /dev/null
+++ b/example/integration_test/app_test.dart
@@ -0,0 +1,89 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:uuid/uuid.dart';
+
+import 'package:device_calendar_example/main.dart' as app;
+
+/// NOTE: These integration tests are currently made to be run on a physical device where there is at least a calendar that can be written to.
+/// Calendar permissions are needed. See example/test_driver/integration_test.dart for how to run this on Android
+void main() {
+  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+  group('Calendar plugin example', () {
+    final eventTitle = Uuid().v1();
+    final saveEventButtonFinder = find.byKey(const Key('saveEventButton'));
+    final eventTitleFinder = find.text(eventTitle);
+    final firstWritableCalendarFinder =
+        find.byKey(const Key('writableCalendar0'));
+    final addEventButtonFinder = find.byKey(const Key('addEventButton'));
+    final titleFieldFinder = find.byKey(const Key('titleField'));
+    final deleteButtonFinder = find.byKey(const Key('deleteEventButton'));
+//TODO: remove redundant restarts. Currently needed because the first screen is always "test starting..."
+    testWidgets('starts on calendars page', (WidgetTester tester) async {
+      app.main();
+      await tester.pumpAndSettle();
+      expect(find.byKey(const Key('calendarsPage')), findsOneWidget);
+    });
+    testWidgets('select first writable calendar', (WidgetTester tester) async {
+      app.main();
+
+      await tester.pumpAndSettle(Duration(milliseconds: 500));
+      expect(firstWritableCalendarFinder, findsOneWidget);
+    });
+    testWidgets('go to add event page', (WidgetTester tester) async {
+      app.main();
+      await tester.pumpAndSettle();
+      await tester.tap(firstWritableCalendarFinder);
+
+      await tester.pumpAndSettle();
+      expect(addEventButtonFinder, findsOneWidget);
+      print('found add event button');
+      await tester.tap(addEventButtonFinder);
+      await tester.pumpAndSettle();
+      expect(saveEventButtonFinder, findsOneWidget);
+    });
+    testWidgets('try to save event without entering mandatory fields',
+        (WidgetTester tester) async {
+      app.main();
+      await tester.pumpAndSettle();
+      await tester.tap(firstWritableCalendarFinder);
+      await tester.pumpAndSettle();
+      await tester.tap(addEventButtonFinder);
+
+      await tester.pumpAndSettle();
+      await tester.tap(saveEventButtonFinder);
+      await tester.pumpAndSettle();
+      expect(find.text('Please fix the errors in red before submitting.'),
+          findsOneWidget);
+    });
+    testWidgets('save event with title $eventTitle',
+        (WidgetTester tester) async {
+      app.main();
+      await tester.pumpAndSettle();
+      await tester.tap(firstWritableCalendarFinder);
+      await tester.pumpAndSettle();
+      await tester.tap(addEventButtonFinder);
+
+      await tester.pumpAndSettle();
+      await tester.tap(titleFieldFinder);
+
+      await tester.enterText(titleFieldFinder, eventTitle);
+      await tester.tap(saveEventButtonFinder);
+      await tester.pumpAndSettle();
+      expect(eventTitleFinder, findsOneWidget);
+    });
+    testWidgets('delete event with title $eventTitle',
+        (WidgetTester tester) async {
+      app.main();
+      await tester.pumpAndSettle();
+      await tester.tap(firstWritableCalendarFinder);
+      await tester.pumpAndSettle();
+      await tester.tap(eventTitleFinder);
+
+      await tester.scrollUntilVisible(deleteButtonFinder, -5);
+      await tester.tap(deleteButtonFinder);
+      await tester.pumpAndSettle();
+      expect(eventTitleFinder, findsNothing);
+    });
+  });
+}
diff --git a/example/integration_test/integration_test.dart b/example/integration_test/integration_test.dart
new file mode 100644
index 00000000..2c840da5
--- /dev/null
+++ b/example/integration_test/integration_test.dart
@@ -0,0 +1,29 @@
+import 'dart:io';
+
+import 'package:integration_test/integration_test_driver.dart';
+
+// make sure 'adb devices' works on your local machine, then from the root of the plugin, run the following:
+/* 
+1.
+cd example
+2.
+flutter drive --driver=integration_test/integration_test.dart --target=integration_test/app_test.dart 
+  */
+
+Future<void> main() async {
+  await Process.run('adb', [
+    'shell',
+    'pm',
+    'grant',
+    'com.builttoroam.devicecalendarexample',
+    'android.permission.READ_CALENDAR'
+  ]);
+  await Process.run('adb', [
+    'shell',
+    'pm',
+    'grant',
+    'com.builttoroam.devicecalendarexample',
+    'android.permission.WRITE_CALENDAR'
+  ]);
+  await integrationDriver();
+}
diff --git a/example/test_driver/app.dart b/example/test_driver/app.dart
deleted file mode 100644
index d4d33a8f..00000000
--- a/example/test_driver/app.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-import 'package:flutter_driver/driver_extension.dart';
-import 'package:device_calendar_example/main.dart' as app;
-
-void main() {
-  // This line enables the extension.
-  enableFlutterDriverExtension();
-
-  // Call the `main()` function of the app, or call `runApp` with
-  // any widget you are interested in testing.
-  app.main();
-}
diff --git a/example/test_driver/app_test.dart b/example/test_driver/app_test.dart
deleted file mode 100644
index 8c9afeae..00000000
--- a/example/test_driver/app_test.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-import 'dart:io';
-
-import 'package:flutter_driver/flutter_driver.dart';
-import 'package:test/test.dart';
-import 'package:uuid/uuid.dart';
-
-/// NOTE: These integration tests are currently made to be run on a physical Android device where there is at least a calendar that can be written to.
-/// They will currently need to be run on a Mac as well
-void main() {
-  group('Calendar plugin example', () {
-    FlutterDriver? driver;
-    final eventTitle = Uuid().v1();
-    final saveEventButtonFinder = find.byValueKey('saveEventButton');
-    final eventTitleFinder = find.text(eventTitle);
-    setUpAll(() async {
-      // workaround for handling permissions based on info taken from https://github.com/flutter/flutter/issues/12561
-      // this is to be run in a Mac environment
-      final envVars = Platform.environment;
-      final adbPath = envVars['ANDROID_HOME'] != null
-          ? envVars['ANDROID_HOME']! + '/platform-tools/adb'
-          : '';
-      await Process.run(adbPath, [
-        'shell',
-        'pm',
-        'grant',
-        'com.builttoroam.devicecalendarexample',
-        'android.permission.INTERNET'
-      ]);
-      await Process.run(adbPath, [
-        'shell',
-        'pm',
-        'grant',
-        'com.builttoroam.devicecalendarexample',
-        'android.permission.READ_CALENDAR'
-      ]);
-      await Process.run(adbPath, [
-        'shell',
-        'pm',
-        'grant',
-        'com.builttoroam.devicecalendarexample',
-        'android.permission.WRITE_CALENDAR'
-      ]);
-
-      driver = await FlutterDriver.connect();
-    });
-
-    tearDownAll(() async {
-      await driver?.close();
-    });
-
-    test('check flutter driver health', () async {
-      final health = await driver?.checkHealth();
-      print('flutter driver status: ${health?.status}');
-    });
-
-    test('starts on calendars page', () async {
-      await driver?.waitFor(find.byValueKey('calendarsPage'));
-    });
-    test('select first writable calendar', () async {
-      final writableCalendarFinder = find.byValueKey('writableCalendar0');
-      await driver?.waitFor(writableCalendarFinder,
-          timeout: Duration(milliseconds: 500));
-      await driver?.tap(writableCalendarFinder);
-    });
-    test('go to add event page', () async {
-      final addEventButtonFinder = find.byValueKey('addEventButton');
-      await driver?.waitFor(addEventButtonFinder);
-      print('found add event button');
-      await driver?.tap(addEventButtonFinder);
-      await driver?.waitFor(saveEventButtonFinder);
-    });
-    test('try to save event without entering mandatory fields', () async {
-      await driver?.tap(saveEventButtonFinder);
-      await driver?.waitFor(
-          find.text('Please fix the errors in red before submitting.'));
-    });
-    test('save event with title $eventTitle', () async {
-      final titleFieldFinder = find.byValueKey('titleField');
-      await driver?.waitFor(titleFieldFinder);
-      await driver?.tap(titleFieldFinder);
-      await driver?.enterText(eventTitle);
-      await driver?.tap(saveEventButtonFinder);
-      await driver?.waitFor(eventTitleFinder);
-    });
-    test('delete event with title $eventTitle', () async {
-      await driver?.tap(eventTitleFinder);
-      final deleteButtonFinder = find.byValueKey('deleteEventButton');
-      await driver?.scrollIntoView(deleteButtonFinder);
-      await driver?.tap(deleteButtonFinder);
-      await driver?.waitForAbsent(eventTitleFinder);
-    });
-  });
-}
diff --git a/test/device_calendar_test.dart b/test/device_calendar_test.dart
index 9973509a..3c518cfd 100644
--- a/test/device_calendar_test.dart
+++ b/test/device_calendar_test.dart
@@ -176,24 +176,64 @@ void main() {
     expect(newAttendee.androidAttendeeDetails, isNull);
   });
 
-  test('Event_Serialises_Correctly', () async {
-    final event = Event('calendarId',
+  test('Event_Serializes_Correctly', () async {
+    final startTime = TZDateTime(
+        timeZoneDatabase.locations.entries.skip(20).first.value,
+        1980,
+        10,
+        1,
+        0,
+        0,
+        0);
+    final endTime = TZDateTime(
+        timeZoneDatabase.locations.entries.skip(21).first.value,
+        1980,
+        10,
+        2,
+        0,
+        0,
+        0);
+    final attendee = Attendee(
+        name: 'Test Attendee',
+        emailAddress: 'test@t.com',
+        role: AttendeeRole.Required,
+        isOrganiser: true);
+    final recurrence = RecurrenceRule(RecurrenceFrequency.Daily);
+    final reminder = Reminder(minutes: 10);
+    var event = Event('calendarId',
         eventId: 'eventId',
-        start: TZDateTime(
-            timeZoneDatabase.locations.entries.skip(20).first.value,
-            1980,
-            10,
-            1,
-            0,
-            0,
-            0));
+        title: 'Test Event',
+        start: startTime,
+        location: 'Seattle, Washington',
+        url: Uri.dataFromString('http://www.example.com'),
+        end: endTime,
+        attendees: [attendee],
+        description: 'Test description',
+        recurrenceRule: recurrence,
+        reminders: [reminder],
+        availability: Availability.Busy);
+
     final stringEvent = event.toJson();
     expect(stringEvent, isNotNull);
     final newEvent = Event.fromJson(stringEvent);
     expect(newEvent, isNotNull);
     expect(newEvent.calendarId, equals(event.calendarId));
     expect(newEvent.eventId, equals(event.eventId));
+    expect(newEvent.title, equals(event.title));
     expect(newEvent.start!.millisecondsSinceEpoch,
         equals(event.start!.millisecondsSinceEpoch));
+    expect(newEvent.end!.millisecondsSinceEpoch,
+        equals(event.end!.millisecondsSinceEpoch));
+    expect(newEvent.description, equals(event.description));
+    expect(newEvent.url, equals(event.url));
+    expect(newEvent.location, equals(event.location));
+    expect(newEvent.attendees, isNotNull);
+    expect(newEvent.attendees?.length, equals(1));
+    expect(newEvent.recurrenceRule, isNotNull);
+    expect(newEvent.recurrenceRule?.recurrenceFrequency,
+        equals(event.recurrenceRule?.recurrenceFrequency));
+    expect(newEvent.reminders, isNotNull);
+    expect(newEvent.reminders?.length, equals(1));
+    expect(newEvent.availability, equals(event.availability));
   });
 }
diff --git a/test_driver/app.dart b/test_driver/app.dart
deleted file mode 100644
index 8b137891..00000000
--- a/test_driver/app.dart
+++ /dev/null
@@ -1,1 +0,0 @@
-