0

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?

Hyejung
  • 902
  • 1
  • 8
  • 22
  • In your background service, you can send updates to the UI using the invoke method, and in your UI, you can listen for these updates using the onDataReceived stream – esQmo_ Aug 30 '23 at 15:34

0 Answers0