I count step from background service in order to count steps when app is inactive also with flutter.
So I use flutter_background_service
package.
And in onStart
method, I call stepEventCount.initializeStepCount()
.
class StepNotification {
Future<void> initializeService() async {
final service = FlutterBackgroundService();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
notificationChannelId,
notificationChannelName,
description: "This channel is used for step notification.",
importance: Importance.low,
playSound: false,
enableVibration: false,
);
final FlutterLocalNotificationsPlugin flutterLocalNotifiationPlugin =
FlutterLocalNotificationsPlugin();
if (Platform.isIOS || Platform.isAndroid) {
await flutterLocalNotifiationPlugin.initialize(
const InitializationSettings(
iOS: DarwinInitializationSettings(),
android: AndroidInitializationSettings('ic_bg_service_small'),
),
);
}
await flutterLocalNotifiationPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await service.configure(
androidConfiguration: AndroidConfiguration(
foregroundServiceNotificationId: notificationId,
onStart: onStart,
autoStart: true,
notificationChannelId: notificationChannelId,
initialNotificationTitle: "오늘도청춘",
initialNotificationContent: "",
isForegroundMode: true,
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
service.startService();
}
}
// background
Future<void> onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
await Firebase.initializeApp();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
StepEventCount stepEventCount = StepEventCount();
StepNotifier stepNotifier = StepNotifier();
StepRepository stepRepository = StepRepository();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
service.on('stopService').listen((event) {
service.stopSelf();
});
}
// background task
stepEventCount.initializeStepCount();
// 날짜 변경
// datePassedResetStep(stepNotifier.todayStepCount);
Timer.periodic(
const Duration(
seconds: 1,
), (timer) async {
if (service is AndroidServiceInstance) {
flutterLocalNotificationsPlugin.show(
notificationId,
"${stepNotifier.todayStepCount} 걸음",
"",
const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
notificationChannelName,
showWhen: false,
ongoing: true,
autoCancel: false,
),
),
);
}
});
service.invoke("update", {"current_date": DateTime.now().toIso8601String()});
}
This method is coming from package pedometer
in flutter.
class StepEventCount {
late Stream<StepCount> stepCountStream;
late Stream<PedestrianStatus> pedestrianStatusStream;
final StepRepository _stepRepository = StepRepository();
final dummyCheck = ValueNotifier(false);
void onStepCount(StepCount event) async {
int totalStepCount = event.steps;
if (dummyCheck.value) {
stepNotifier.incrementStepCount();
} else {
await _stepRepository.updateDummyField(totalStepCount);
await _stepRepository.updateTodayStepCount(0);
}
}
void onPedestrianStatusChanged(PedestrianStatus event) {}
void onPedestrianStatusError(error) {}
void onStepCountError(error) {}
void initializeStepCount() async {
dummyCheck.value = await _stepRepository.dummyExistsCheck();
pedestrianStatusStream = Pedometer.pedestrianStatusStream;
stepCountStream = Pedometer.stepCountStream;
stepCountStream.listen(onStepCount).onError(onStepCountError);
pedestrianStatusStream
.listen(onPedestrianStatusChanged)
.onError(onPedestrianStatusError);
}
}
so when step event appears, I update stepNotifier.incrementStepCount();
This is changeNotifier
.
class StepNotifier extends ChangeNotifier {
int _todayStepCount = 0;
int get todayStepCount => _todayStepCount;
StepNotifier() {
initializeTodayStep();
}
Future<void> initializeTodayStep() async {
_todayStepCount = await StepRepository().fetchUserTodayStepCount();
notifyListeners();
}
void incrementStepCount() {
print("incrementStepCount");
++_todayStepCount;
notifyListeners();
}
void resetStepCount() async {
_todayStepCount = 0;
notifyListeners();
}
}
final stepNotifier = StepNotifier();
And I listen this Value with AnimatedBuilder in UI.
AnimatedBuilder(
animation: stepNotifier,
builder: (context, child) => Text(
"${stepNotifier.todayStepCount} 보",
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w600,
),
),
)
The problem is here, UI is not updated with new step count.
I double check with other case.
When I tap GestureDetector I call incrementStepCount()
of StepNotifier.
And it updates UI perfect.
And i also check that when step event happens, this notifier calls incrementStepCount() method well as printing log.
I guess this issue is related to thread..?
Can you explain why and how to fix it in detail?