戴余标 2 years ago
parent 2d63a24fb1
commit 63a2277119

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

@ -0,0 +1,236 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="characters">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
</list>
</value>
</entry>
<entry key="clock">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
</list>
</value>
</entry>
<entry key="cupertino_icons">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="datang">
<value>
<list>
<option value="$USER_HOME$/lib" />
</list>
</value>
</entry>
<entry key="english_words">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/english_words-4.0.0/lib" />
</list>
</value>
</entry>
<entry key="fake_async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="flutter">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/packages/flutter/lib" />
</list>
</value>
</entry>
<entry key="flutter_lints">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="flutter_test">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/packages/flutter_test/lib" />
</list>
</value>
</entry>
<entry key="lints">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib" />
</list>
</value>
</entry>
<entry key="material_color_utilities">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib" />
</list>
</value>
</entry>
<entry key="platform">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
</list>
</value>
</entry>
<entry key="sky_engine">
<value>
<list>
<option value="$USER_HOME$/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
</list>
</value>
</entry>
<entry key="telephony">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/telephony-0.2.0/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
</list>
</value>
</entry>
<entry key="test_api">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib" />
</list>
</value>
</entry>
<entry key="vector_math">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.8.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/characters-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.16.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/cupertino_icons-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/english_words-4.0.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/fake_async-1.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/flutter_lints-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/lints-1.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.11/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/material_color_utilities-0.1.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.8.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/platform-3.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/telephony-0.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.9/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.2/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/pkg/sky_engine/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/packages/flutter/lib" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/packages/flutter_test/lib" />
<root url="file://$USER_HOME$/lib" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,27 @@
<component name="libraryTable">
<library name="Dart SDK">
<CLASSES>
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/async" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/cli" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/collection" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/convert" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/core" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/developer" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/ffi" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/html" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/indexed_db" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/io" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/isolate" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/js" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/js_util" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/math" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/mirrors" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/svg" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/typed_data" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/web_audio" />
<root url="file://$USER_HOME$/fvm/versions/3.0.0/bin/cache/dart-sdk/lib/web_gl" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,7 @@
<component name="libraryTable">
<library name="Flutter Plugins" type="FlutterPluginsLibraryType">
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/flutter_background_service_android.iml" filepath="$PROJECT_DIR$/.idea/flutter_background_service_android.iml" />
</modules>
</component>
</project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

@ -0,0 +1,180 @@
## 2.0.3
- **FIX**: wakelock not released. ([e427f3b7](https://github.com/ekasetiawans/flutter_background_service/commit/e427f3b70138ec26f9671c2617f9061f25eade6f))
## 2.0.2
- **FIX**: autoStartOnBootMode #160. ([16a785a3](https://github.com/ekasetiawans/flutter_background_service/commit/16a785a3cbcb4226321ddddf681b6554196fa4db))
## 2.0.1
- **FIX**: release wakelock. ([c0830250](https://github.com/ekasetiawans/flutter_background_service/commit/c0830250b90a1ba6e2543a1bb25a13fba59a56b7))
## 2.0.0
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
## 2.0.0-dev.0
> Note: This release has breaking changes.
- **BREAKING** **FEAT**: implement new concept. ([c8ce9c0b](https://github.com/ekasetiawans/flutter_background_service/commit/c8ce9c0bab82137dea031af124b84510286661f7))
## 1.0.2
- **DOCS**: readme link. ([1479b91c](https://github.com/ekasetiawans/flutter_background_service/commit/1479b91cd80d637335de1314a528bcf51ebb7c0f))
## 1.0.1
- **DOCS**: update README. ([fbf5e0ab](https://github.com/ekasetiawans/flutter_background_service/commit/fbf5e0abeeb9296ba32361b8af0a298ee9e71527))
## 0.0.2
- **FEAT**: migrate to plugin platform interface. ([70e08ff0](https://github.com/ekasetiawans/flutter_background_service/commit/70e08ff03232c31946cc8eb7896f69c830f23322))
## 0.0.1+3
- **FIX**: errors. ([13a6f841](https://github.com/ekasetiawans/flutter_background_service/commit/13a6f841f5d677ceb0010e8ba1bf9d7af53adbcf))
## 0.0.1+2
- Update a dependency to the latest release.
## 0.0.1+1
- **REFACTOR**: initialize melos.
## 0.2.6
* FIX: (Android) flutter initialization
## 0.2.5
* FIX: (iOS) using other plugins
## 0.2.4
* FIX: (Android) run service background when charger not connected and screen lock (#92)
## 0.2.3
* ADDED: Using `BGTaskScheduler` on iOS 13. See readme for configuration.
## 0.2.2
* ADDED: `autoStart` to `IosConfiguration`
## 0.2.1
* UPDATE README
* UPDATE: Flutter Version Constraint
## 0.2.0+1
* UPDATE README
## 0.2.0
* [BREAKING]: FlutterBackgroundService.initialize renamed to FlutterBackgroundService.configure
* [BREAKING]: use FlutterBackgroundService.start to start or restart after you call stopService.
* [ADDED]: IOS Background fetch is now supported you have to enable background fetch from xcode.
## 0.1.7
* Fix : cannot start service on android 12
* Fix : not started on boot completed
## 0.1.6
* Android 12 Compatibility Changes
## 0.1.5
* Rollback foreground notification importance
## 0.1.4
* fixes UnsatisfiedLinkError when running as foreground service with autostart #32
## 0.1.3
* Fix notification not showing on android 7 and prior (Issue #26)
## 0.1.2
* Open app from notification (Issue #30)
## 0.1.1
* Fix #29 (DartVM not terminated when service stop)
## 0.1.0
* Bump flutter 2
## 0.1.0-nullsafety.2
* Fix #23
## 0.1.0-nullsafety.1
* Added isServiceRunning on iOS (issue #19)
## 0.1.0-nullsafety.0
* Added support to nullsafety
## 0.0.1+18
* Added stopService Method(Currently Works on Android Only).
## 0.0.1+17
* Add preference autoStart on Boot, default is true.
## 0.0.1+16
* Set Foreground Mode to false will remove notification. BugFix #4.
## 0.0.1+15
* Add ability to change Background or Foreground mode (Android Only)
## 0.0.1+14
* Bugfix BootReceiver
## 0.0.1+13
* Update example for iOS support.
## 0.0.1+12
* Start service immediately after initialize
## 0.0.1+11
* iOS
## 0.0.1+10
* bug fix
## 0.0.1+9
* bug fix
## 0.0.1+8
* bug fix
## 0.0.1+7
* Add ability to send data from UI to Service
## 0.0.1+6
* Improve stability
## 0.0.1+5
* Add ability to send data from service to UI
## 0.0.1+4
* Update README
## 0.0.1+3
* Add ability to change notification info (Android foreground service)
## 0.0.1+2
* Fix android missing plugin implementation
## 0.0.1+1
* Fix android build
## 0.0.1
* TODO: Describe initial release.

@ -0,0 +1,25 @@
Copyright 2017 The Chromium Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,37 @@
group 'id.flutter.flutter_background_service'
version '1.0'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion 16
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
}

@ -0,0 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

@ -0,0 +1,6 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

@ -0,0 +1 @@
rootProject.name = 'flutter_background_service'

@ -0,0 +1,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="id.flutter.flutter_background_service">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application>
<service
android:enabled="true"
android:exported="true"
android:name=".BackgroundService"
/>
<receiver
android:name=".WatchdogReceiver"
android:enabled="true"
android:exported="true"
/>
<receiver android:name=".BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>

@ -0,0 +1,327 @@
package id.flutter.flutter_background_service;
import static android.os.Build.VERSION.SDK_INT;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.AlarmManagerCompat;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.concurrent.atomic.AtomicBoolean;
import java.lang.UnsatisfiedLinkError;
import io.flutter.FlutterInjector;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.plugin.common.JSONMethodCodec;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.view.FlutterCallbackInformation;
public class BackgroundService extends Service implements MethodChannel.MethodCallHandler {
private static final String TAG = "BackgroundService";
private FlutterEngine backgroundEngine;
private MethodChannel methodChannel;
private DartExecutor.DartCallback dartCallback;
private boolean isManuallyStopped = false;
String notificationTitle = "短信帮手正在后台运行";
String notificationContent = "Running";
private static final String LOCK_NAME = BackgroundService.class.getName()
+ ".Lock";
public static volatile WakeLock lockStatic = null; // notice static
synchronized public static PowerManager.WakeLock getLock(Context context) {
if (lockStatic == null) {
PowerManager mgr = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.FULL_WAKE_LOCK,
LOCK_NAME);
lockStatic.setReferenceCounted(true);
}
return (lockStatic);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static void enqueue(Context context) {
Intent intent = new Intent(context, WatchdogReceiver.class);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags |= PendingIntent.FLAG_MUTABLE;
}
PendingIntent pIntent = PendingIntent.getBroadcast(context, 111, intent, flags);
AlarmManagerCompat.setAndAllowWhileIdle(manager, AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000, pIntent);
}
public void setAutoStartOnBootMode(boolean value) {
SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
pref.edit().putBoolean("auto_start_on_boot", value).apply();
}
public static boolean isAutoStartOnBootMode(Context context) {
SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
return pref.getBoolean("auto_start_on_boot", true);
}
public void setForegroundServiceMode(boolean value) {
SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
pref.edit().putBoolean("is_foreground", value).apply();
}
public static boolean isForegroundService(Context context) {
SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
return pref.getBoolean("is_foreground", true);
}
public void setManuallyStopped(boolean value) {
SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
pref.edit().putBoolean("is_manually_stopped", value).apply();
}
public static boolean isManuallyStopped(Context context) {
SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
return pref.getBoolean("is_manually_stopped", false);
}
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
notificationContent = "点击即可查看更多选项。";
updateNotificationInfo();
}
@Override
public void onDestroy() {
if (!isManuallyStopped) {
enqueue(this);
} else {
setManuallyStopped(true);
}
stopForeground(true);
isRunning.set(false);
if (backgroundEngine != null) {
backgroundEngine.getServiceControlSurface().detachFromService();
backgroundEngine.destroy();
backgroundEngine = null;
}
methodChannel = null;
dartCallback = null;
super.onDestroy();
}
private void createNotificationChannel() {
if (SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Background Service";
String description = "Executing process in background";
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel channel = new NotificationChannel("FOREGROUND_DEFAULT", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
protected void updateNotificationInfo() {
if (isForegroundService(this)) {
String packageName = getApplicationContext().getPackageName();
Intent i = getPackageManager().getLaunchIntentForPackage(packageName);
int flags = PendingIntent.FLAG_CANCEL_CURRENT;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags |= PendingIntent.FLAG_MUTABLE;
}
PendingIntent pi = PendingIntent.getActivity(BackgroundService.this, 99778, i, flags);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "FOREGROUND_DEFAULT")
.setSmallIcon(R.drawable.ic_bg_service_small)
.setAutoCancel(true)
.setOngoing(true)
.setContentTitle(notificationTitle)
.setContentText(notificationContent)
.setContentIntent(pi);
startForeground(99778, mBuilder.build());
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
setManuallyStopped(false);
enqueue(this);
runService();
return START_STICKY;
}
AtomicBoolean isRunning = new AtomicBoolean(false);
private void runService() {
try {
Log.d(TAG, "runService");
if (isRunning.get() || (backgroundEngine != null && !backgroundEngine.getDartExecutor().isExecutingDart()))
return;
if (lockStatic == null){
getLock(getApplicationContext()).acquire(10*60*1000L /*10 minutes*/);
}
updateNotificationInfo();
SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
long entrypointHandle = pref.getLong("entrypoint_handle", 0);
FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader();
// initialize flutter if it's not initialized yet
if (!flutterLoader.initialized()) {
flutterLoader.startInitialization(getApplicationContext());
}
flutterLoader.ensureInitializationComplete(getApplicationContext(), null);
FlutterCallbackInformation callback = FlutterCallbackInformation.lookupCallbackInformation(entrypointHandle);
if (callback == null) {
Log.e(TAG, "callback handle not found");
return;
}
isRunning.set(true);
backgroundEngine = new FlutterEngine(this);
backgroundEngine.getServiceControlSurface().attachToService(BackgroundService.this, null, isForegroundService(this));
methodChannel = new MethodChannel(backgroundEngine.getDartExecutor().getBinaryMessenger(), "id.flutter/background_service_android_bg", JSONMethodCodec.INSTANCE);
methodChannel.setMethodCallHandler(this);
dartCallback = new DartExecutor.DartCallback(getAssets(), flutterLoader.findAppBundlePath(), callback);
backgroundEngine.getDartExecutor().executeDartCallback(dartCallback);
} catch (UnsatisfiedLinkError e) {
notificationContent = "Error " + e.getMessage();
updateNotificationInfo();
Log.w(TAG, "UnsatisfiedLinkError: After a reboot this may happen for a short period and it is ok to ignore then!" + e.getMessage());
}
}
public void receiveData(JSONObject data) {
if (methodChannel != null) {
try {
methodChannel.invokeMethod("onReceiveData", data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
String method = call.method;
try {
if (method.equalsIgnoreCase("getHandler")) {
SharedPreferences pref = getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
long backgroundHandle = pref.getLong("background_handle", 0);
result.success(backgroundHandle);
if (lockStatic != null) {
lockStatic.release();
lockStatic = null;
}
return;
}
if (method.equalsIgnoreCase("setNotificationInfo")) {
JSONObject arg = (JSONObject) call.arguments;
if (arg.has("title")) {
notificationTitle = arg.getString("title");
notificationContent = arg.getString("content");
updateNotificationInfo();
result.success(true);
}
return;
}
if (method.equalsIgnoreCase("setAutoStartOnBootMode")) {
JSONObject arg = (JSONObject) call.arguments;
boolean value = arg.getBoolean("value");
setAutoStartOnBootMode(value);
result.success(true);
return;
}
if (method.equalsIgnoreCase("setForegroundMode")) {
JSONObject arg = (JSONObject) call.arguments;
boolean value = arg.getBoolean("value");
setForegroundServiceMode(value);
if (value) {
updateNotificationInfo();
} else {
stopForeground(true);
}
result.success(true);
return;
}
if (method.equalsIgnoreCase("stopService")) {
isManuallyStopped = true;
Intent intent = new Intent(this, WatchdogReceiver.class);
int flags = PendingIntent.FLAG_CANCEL_CURRENT;
if (SDK_INT >= Build.VERSION_CODES.S) {
flags |= PendingIntent.FLAG_MUTABLE;
}
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 111, intent, flags);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pi);
stopSelf();
result.success(true);
return;
}
if (method.equalsIgnoreCase("sendData")) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
Intent intent = new Intent("id.flutter/background_service");
intent.putExtra("data", call.arguments.toString());
manager.sendBroadcast(intent);
result.success(true);
return;
}
} catch (JSONException e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
result.notImplemented();
}
}

@ -0,0 +1,30 @@
package id.flutter.flutter_background_service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import androidx.core.content.ContextCompat;
import static android.content.Context.MODE_PRIVATE;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
boolean autoStart = pref.getBoolean("auto_start_on_boot",true);
if(autoStart) {
if (BackgroundService.lockStatic == null){
BackgroundService.getLock(context).acquire(10*60*1000L /*10 minutes*/);
}
if (BackgroundService.isForegroundService(context)) {
ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
} else {
context.startService(new Intent(context, BackgroundService.class));
}
}
}
}

@ -0,0 +1,183 @@
package id.flutter.flutter_background_service;
import static android.content.Context.MODE_PRIVATE;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.service.ServiceAware;
import io.flutter.embedding.engine.plugins.service.ServicePluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.common.JSONMethodCodec;
/** FlutterBackgroundServicePlugin */
public class FlutterBackgroundServicePlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware {
private static final String TAG = "BackgroundServicePlugin";
private static final List<FlutterBackgroundServicePlugin> _instances = new ArrayList<>();
public FlutterBackgroundServicePlugin() {
_instances.add(this);
}
private MethodChannel channel;
private Context context;
private BackgroundService service;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
this.context = flutterPluginBinding.getApplicationContext();
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
localBroadcastManager.registerReceiver(this, new IntentFilter("id.flutter/background_service"));
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "id.flutter/background_service_android", JSONMethodCodec.INSTANCE);
channel.setMethodCallHandler(this);
}
public static void registerWith(Registrar registrar) {
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(registrar.context());
final FlutterBackgroundServicePlugin plugin = new FlutterBackgroundServicePlugin();
localBroadcastManager.registerReceiver(plugin, new IntentFilter("id.flutter/background_service"));
final MethodChannel channel = new MethodChannel(registrar.messenger(), "id.flutter/background_service_android", JSONMethodCodec.INSTANCE);
channel.setMethodCallHandler(plugin);
plugin.channel = channel;
}
private static void configure(Context context, long entrypointHandle, long backgroundHandle, boolean isForeground, boolean autoStartOnBoot) {
SharedPreferences pref = context.getSharedPreferences("id.flutter.background_service", MODE_PRIVATE);
pref.edit()
.putLong("entrypoint_handle", entrypointHandle)
.putLong("background_handle", backgroundHandle)
.putBoolean("is_foreground", isForeground)
.putBoolean("auto_start_on_boot", autoStartOnBoot)
.apply();
}
private void start() {
BackgroundService.enqueue(context);
boolean isForeground = BackgroundService.isForegroundService(context);
Intent intent = new Intent(context, BackgroundService.class);
if (isForeground) {
ContextCompat.startForegroundService(context, intent);
} else {
context.startService(intent);
}
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
String method = call.method;
JSONObject arg = (JSONObject) call.arguments;
try {
if ("configure".equals(method)) {
long entrypointHandle = arg.getLong("entrypoint_handle");
long backgroundHandle = arg.getLong("background_handle");
boolean isForeground = arg.getBoolean("is_foreground_mode");
boolean autoStartOnBoot = arg.getBoolean("auto_start_on_boot");
configure(context, entrypointHandle, backgroundHandle, isForeground, autoStartOnBoot);
if (autoStartOnBoot) {
start();
}
result.success(true);
return;
}
if ("start".equals(method)) {
start();
result.success(true);
return;
}
if (method.equalsIgnoreCase("sendData")) {
for (FlutterBackgroundServicePlugin plugin : _instances) {
if (plugin.service != null) {
plugin.service.receiveData((JSONObject) call.arguments);
break;
}
}
result.success(true);
return;
}
if (method.equalsIgnoreCase("isServiceRunning")) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (BackgroundService.class.getName().equals(service.service.getClassName())) {
result.success(true);
return;
}
}
result.success(false);
return;
}
result.notImplemented();
} catch (Exception e) {
result.error("100", "Failed read arguments", null);
}
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this.context);
localBroadcastManager.unregisterReceiver(this);
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) return;
if (intent.getAction().equalsIgnoreCase("id.flutter/background_service")) {
String data = intent.getStringExtra("data");
try {
JSONObject jData = new JSONObject(data);
if (channel != null) {
channel.invokeMethod("onReceiveData", jData);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onAttachedToService(@NonNull ServicePluginBinding binding) {
Log.d(TAG, "onAttachedToService");
this.service = (BackgroundService) binding.getService();
}
@Override
public void onDetachedFromService() {
this.service = null;
Log.d(TAG, "onDetachedFromService");
}
}

@ -0,0 +1,20 @@
package id.flutter.flutter_background_service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import androidx.core.content.ContextCompat;
public class WatchdogReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(!BackgroundService.isManuallyStopped(context)) {
if (BackgroundService.isForegroundService(context)) {
ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
} else {
context.startService(new Intent(context, BackgroundService.class));
}
}
}
}

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
<group android:scaleX="1.38"
android:scaleY="1.38"
android:translateX="-4.56"
android:translateY="-4.56">
<path
android:fillColor="@android:color/white"
android:pathData="M6.05,8.05c-2.73,2.73 -2.73,7.15 -0.02,9.88c1.47,-3.4 4.09,-6.24 7.36,-7.93c-2.77,2.34 -4.71,5.61 -5.39,9.32c2.6,1.23 5.8,0.78 7.95,-1.37C19.43,14.47 20,4 20,4S9.53,4.57 6.05,8.05z"/>
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>

@ -0,0 +1,186 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_service_platform_interface/flutter_background_service_platform_interface.dart';
Future<void> _entrypoint() async {
WidgetsFlutterBinding.ensureInitialized();
final service = AndroidServiceInstance._();
final int handle = await service._getHandler();
final callbackHandle = CallbackHandle.fromRawHandle(handle);
final onStart = PluginUtilities.getCallbackFromHandle(callbackHandle);
if (onStart != null) {
onStart(service);
}
}
class FlutterBackgroundServiceAndroid extends FlutterBackgroundServicePlatform {
/// Registers this class as the default instance of [FlutterBackgroundServicePlatform].
static void registerWith() {
FlutterBackgroundServicePlatform.instance =
FlutterBackgroundServiceAndroid();
}
static const MethodChannel _channel = const MethodChannel(
'id.flutter/background_service_android',
JSONMethodCodec(),
);
Future<dynamic> _handle(MethodCall call) async {
switch (call.method) {
case "onReceiveData":
_controller.sink.add(call.arguments);
break;
default:
}
return true;
}
Future<bool> start() async {
final result = await _channel.invokeMethod('start');
return result ?? false;
}
Future<bool> configure({
required IosConfiguration iosConfiguration,
required AndroidConfiguration androidConfiguration,
}) async {
_channel.setMethodCallHandler(_handle);
final CallbackHandle? entryPointHandle =
PluginUtilities.getCallbackHandle(_entrypoint);
final CallbackHandle? handle =
PluginUtilities.getCallbackHandle(androidConfiguration.onStart);
if (entryPointHandle == null || handle == null) {
return false;
}
final result = await _channel.invokeMethod(
"configure",
{
"entrypoint_handle": entryPointHandle.toRawHandle(),
"background_handle": handle.toRawHandle(),
"is_foreground_mode": androidConfiguration.isForegroundMode,
"auto_start_on_boot": androidConfiguration.autoStart,
},
);
return result ?? false;
}
Future<bool> isServiceRunning() async {
var result = await _channel.invokeMethod("isServiceRunning");
return result ?? false;
}
final _controller = StreamController.broadcast(sync: true);
void dispose() {
_controller.close();
}
@override
void invoke(String method, [Map<String, dynamic>? args]) {
_channel.invokeMethod("sendData", {
'method': method,
'args': args,
});
}
@override
Stream<Map<String, dynamic>?> on(String method) {
return _controller.stream.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
if (data['method'] == method) {
sink.add(data['args']);
}
},
),
);
}
}
class AndroidServiceInstance extends ServiceInstance {
static const MethodChannel _channel = const MethodChannel(
'id.flutter/background_service_android_bg',
JSONMethodCodec(),
);
AndroidServiceInstance._() {
_channel.setMethodCallHandler(_handleMethodCall);
}
final _controller = StreamController.broadcast(sync: true);
Future<void> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case "onReceiveData":
_controller.sink.add(call.arguments);
break;
default:
}
}
@override
void invoke(String method, [Map<String, dynamic>? args]) {
_channel.invokeMethod('sendData', {
'method': method,
'args': args,
});
}
@override
Future<void> stopSelf() async {
await _channel.invokeMethod("stopService");
}
@override
Stream<Map<String, dynamic>?> on(String method) {
return _controller.stream.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
if (data['method'] == method) {
sink.add(data['args']);
}
},
),
);
}
Future<void> setForegroundNotificationInfo({
required String title,
required String content,
}) async {
await _channel.invokeMethod("setNotificationInfo", {
"title": title,
"content": content,
});
}
Future<void> setAsForegroundService() async {
await _channel.invokeMethod("setForegroundMode", {
'value': true,
});
}
Future<void> setAsBackgroundService() async {
await _channel.invokeMethod("setForegroundMode", {
'value': false,
});
}
Future<int> _getHandler() async {
return await _channel.invokeMethod('getHandler');
}
Future<void> setAutoStartOnBootMode(bool value) async {
await _channel.invokeMethod("setAutoStartOnBootMode", {
"value": value,
});
}
}

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.idea" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/android/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/example/android/.idea" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Pods" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks" />
<excludeFolder url="file://$MODULE_DIR$/example/macos/Flutter" />
<excludeFolder url="file://$MODULE_DIR$/example/macos/Pods" />
<excludeFolder url="file://$MODULE_DIR$/example/macos/.symlinks" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

@ -0,0 +1,63 @@
name: flutter_background_service_android
description: A flutter plugin for executing dart code continously even application closed.
version: 2.0.3
repository: https://github.com/ekasetiawans/flutter_background_service
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
dependencies:
flutter:
sdk: flutter
flutter_background_service_platform_interface: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
implements: flutter_background_service
platforms:
android:
package: id.flutter.flutter_background_service
pluginClass: FlutterBackgroundServicePlugin
dartPluginClass: FlutterBackgroundServiceAndroid
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
Loading…
Cancel
Save