方言を話すおしゃべり猫型ロボット『ミーア』をリリースしました(こちらをクリック)

[WordPress × FCM] How to send new announcements posted on a website to an app.

wordpress-fcm-notification
This article can be read in about 20 minutes.

Introduction.

I am now developing “Mia,” a talking cat-shaped robot that speaks in various dialects.

Mia
The talking cat-shaped robot Mia shares sadness and joy with more than 100 different rich expressions. It also speaks in...


When a new announcement is added, a push notification will be sent to the app, and when the user clicks on the push notification, the user will be redirected to the list of announcements. When a new notification is added, the app will push the notification to the list of notifications.

In other words, we would like to centralize the management of announcements by posting them on our website.

This time, we will attempt to implement a web view display of the list of announcements to be posted on the HP and application push notifications of new announcements.

Web View Display of Notices List

In this case, I would place the notification traffic line at the top of the page in the settings screen, rather than in the footer at the bottom of the screen.

Add “Notices” as a list item on the Settings screen and tap it to display the NoticeScreen.

/lib/screens/home/settings_tab.dart

Dart
import 'package:clocky_app/screens/home/notice_screen.dart';
// 省略

class SettingsTab extends ConsumerStatefulWidget {
  const SettingsTab({super.key});

  @override
  _SettingsTabState createState() => _SettingsTabState();
}

class _SettingsTabState extends ConsumerState {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("設定"),
        automaticallyImplyLeading: false,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16.0),
        children: [
          buildSection(['お知らせ']), // お知らせセクションの追加
          // 他のセクションは省略
        ],
      ),
    );
  }

  Widget buildSection(List items) {
    return Container(
      margin: const EdgeInsets.only(bottom: 24.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(5.0),
        boxShadow: [
          BoxShadow(
            color: Colors.grey.withOpacity(0.2),
            spreadRadius: 1.0,
            blurRadius: 5.0,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children:
            items.map((item) => buildItem(item, items.last == item)).toList(),
      ),
    );
  }

  Widget buildItem(String item, bool isLast) {
    return Container(
      decoration: BoxDecoration(
        border: isLast
            ? null
            : const Border(
                bottom: BorderSide(
                  color: Colors.grey,
                  width: 0.5,
                ),
              ),
      ),
      child: Column(
        children: [
          ListTile(
            title: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  item,
                  style: const TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            trailing: const Icon(Icons.chevron_right),
            onTap: () async {
              if (item == 'お知らせ') {
                showModalBottomSheet(
                  context: context,
                  isScrollControlled: true,
                  enableDrag: false,
                  builder: (_) => const FractionallySizedBox(
                    heightFactor: 0.85,
                    child: NoticeScreen(),
                  ),
                );
              }
            },
          ),
        ],
      ),
    );
  }
}

In notice_screen.dart, WebViewController is initialized and the specified URL ( https://mia-cat.com/category/notice/) is loaded.

/lib/screens/home/notice_screen.dart

Dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class NoticeScreen extends StatefulWidget {
  const NoticeScreen({super.key});

  @override
  _NoticeScreenState createState() => _NoticeScreenState();
}

class _NoticeScreenState extends State {
  late WebViewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..loadRequest(Uri.parse('https://mia-cat.com/category/notice/'));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('お知らせ'),
        leading: Container(),
        actions: [
          IconButton(
            icon: const Icon(Icons.close),
            onPressed: () {
              Navigator.of(context).pop();
            },
          ),
        ],
      ),
      body: WebViewWidget(controller: _controller),
    );
  }
}

After the “Announcements” was added to the top of the settings page, a list of articles posted in the Announcements category of the HP is now displayed in the Web view when clicked.

FCM notification and transition control on tap

As for this one, we first tried to automate it. In other words, when a new post is published in the Notifications category of WordPress, a push notification is automatically sent to the application.

However, since the implementation was somewhat complicated and it is unlikely at this point that we will be notified that often, we decided to switch to sending notifications manually from the Firebase console for the time being.

In a previous article here, I described manually sending test FCM notifications from the Firebase console.

At this time, we did not need to control the screen transition on tap because we just went to the home screen when we tapped the notification, but this time we want the screen to transition to the settings screen, so we will slightly predominate the application code.

Modify main.dart as follows

Subscribe to a topic

All users subscribe to the topic all_users. This means that notifications are sent for the topic all_users will reach all subscribing users.

Dart
FirebaseMessaging.instance.subscribeToTopic('all_users');

Screen transition when tapping a notification

_handleMessage function: Checks whether the key "notice" is included in the data of the received message, and if the value of the notice key is ” details “, the user is taken to the SettingTab screen.

Dart
void _handleMessage(RemoteMessage message) {
    if (message.data.containsKey('notice')) {
      final screen = message.data['notice'];
      if (screen == 'details') {
        navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => const SettingTab()));
      }
    }
}

main.dart Whole

Dart
import 'package:clocky_app/app_switcher.dart';
import 'package:clocky_app/firebase_options.dart';
import 'package:clocky_app/screens/home/settings_tab.dart';
import 'package:clocky_app/styles/colors.dart';
import 'package:clocky_app/styles/text_styles.dart';
import 'package:clocky_app/widgets/show_custom_message_bar.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:overlay_support/overlay_support.dart';

final GlobalKey navigatorKey = GlobalKey();
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // ヘルスコネクトが利用できるようになった後に有効化する
  // await initializeBackgroundService();
  runApp(const ProviderScope(child: ClockyApp()));
}

class ClockyApp extends ConsumerStatefulWidget {
  const ClockyApp({super.key});

  @override
  _ClockyAppState createState() => _ClockyAppState();
}

class _ClockyAppState extends ConsumerState {
  @override
  void initState() {
    super.initState();
    initializeApp();
    setupFirebaseMessaging();
  }

  Future initializeApp() async {
    // フォアグラウンドでのPush通知
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      if (message.notification != null) {
        showCustomMessageBar(
          context: navigatorKey.currentContext!,
          title: message.notification!.title ?? "通知",
          message: message.notification!.body ?? "新しい通知",
          onClose: () {
            debugPrint("Notification closed");
          },
          onTapped: () {
            debugPrint("Notification tapped");
            _handleMessage(message);
          },
          duration: Duration.zero,
        );
      }
    });
  }

  void setupFirebaseMessaging() {
    FirebaseMessaging.instance.subscribeToTopic('all_users');

    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      _handleMessage(message);
    });

    FirebaseMessaging.instance
        .getInitialMessage()
        .then((RemoteMessage? message) {
      if (message != null) {
        _handleMessage(message);
      }
    });
  }

  void _handleMessage(RemoteMessage message) {
    if (message.data.containsKey('notice')) {
      final screen = message.data['notice'];
      if (screen == 'details') {
        navigatorKey.currentState
            ?.push(MaterialPageRoute(builder: (_) => const SettingsTab()));
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return OverlaySupport.global(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        navigatorKey: navigatorKey,
        title: 'Clocky App',
        theme: ThemeData(
          colorScheme: const ColorScheme.light(
            primary: AppColors.primaryColor,
            secondary: AppColors.primaryColorDark,
            background: AppColors.backgroundColor,
            onBackground: AppColors.textColor,
          ),
          textTheme: const TextTheme(
            displayLarge: TextStyles.titleStyle,
            bodyLarge: TextStyles.bodyStyle,
          ),
        ),
        routes: {
          '/': (context) => const AppSwitcher(),
        },
      ),
    );
  }
}

Configuration on the Firebase console screen

By entering the following on the Firebase console screen, the app will recognize this FCM notification and will be able to move to the settings screen when the notification is tapped.

  • Topic: all_users
  • (custom data) key: notice, value: details

operation check

Publish an article on the recently implemented mute function by selecting the category “Announcements”.

After publishing, open the Firebase console and enter the notification title text as shown below.

Then, in the target, enter topic → all_users.

Select Send Now, and in the Custom Data column of the Other Options

  • Key: NOTICE
  • Value: detail

and click “Confirm

Click “Publish” when a pop-up window appears asking you to reconfirm.

foreground

background

When you tap a notification

We confirmed that notifications appear successfully both in the foreground and in the background, tap to go to the settings screen, and click on Notifications to see the list of notifications.

In actual operation, by duplicating the first FCM notification, the notification conditions (topic and custom data keys and values) will also be retained, so it would be a good idea to duplicate it, change only the message title, and distribute it.

Although it is manual, it seems to be surprisingly hassle-free so that we will try this operation for the time being.

Copied title and URL