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

[Flutter] Display daily GIF images and text on the top screen.

flutter-display-daily-gif-images
この記事は約16分で読めます。

Introduction.

Developing “Mia,” a talking cat-shaped robot that speaks dialects.

https://mia-cat.com/en

The current top screen of the application is shown below.
It only shows a still image of Mia as of August 2023, when she was still in prototype form.

The following implementation will be added so that users can open and enjoy the application more every day.

  • Mia’s still image to GIF animation
  • Displays “Mia’s Word of the Day” in addition to GIF animation
  • GIF animations and one word phrases are displayed on a daily basis to allow users to enjoy the daily changes.

Prepare GIF images and text

First, a list of GIF images and text to be displayed must be prepared.

As for GIF images, Mia provides a variety of eye expression images, and in the following article, we have created a function that allows you to upload eye images to verify the operation and download GIFs as is.

I created some GIF images like this.

GIF image in pubspec.yaml

When using images or other assets in a Flutter project, they should be noted in the pubspec.yaml to inform Flutter. This will allow Flutter to include the assets in the app’s asset bundle during the build process.

Place GIF images under assets/images and include them in pubspec.yaml.

Add an assets section to pubspec.yaml and include the path to the images. If you want to add all images in a folder at once, describe the directory path and add / at the end to include all files in the folder.

Dart
flutter:
  assets:
    - assets/images/

Or, if only certain files are to be added explicitly, the file names should be listed individually.

Dart
flutter:
  assets:
    - assets/images/specific_image.gif
    - assets/images/another_image.gif

After you have written it in pubspec.yaml, run flutter pub get to make the changes take effect. Now you can load assets using widgets such as Image.asset. In this case, since we will be importing many gif images, we specify the images directory as assets.

Implemented a function to get daily messages and GIFs

Separate functions for images and text into separate files and call them from the top screen. create a new file called daily_message_service.dart and implement functions to retrieve daily messages and GIFs in it.

daily_message_service.dart will be the service class responsible for the business logic of the application, so it is listed in the services directory.

YAML
lib/
└── services/
    └── daily_message_service.dart

Save and load selected items

  • Save the index of selected GIFs and text to local storage (e.g., SharedPreferences ) so that the same items can be displayed when the app is reopened on the same day.
  • Use Dart’s DateTime class to get the current date and use that date as the seed value in the Random class. This ensures that the same item is always selected when the application is opened on the same date.

Detect date changes

  • Check the current date when the user opens the app and select a new item to save if it differs from the last saved date.
Dart
// lib/services/daily_message_service.dart
import 'dart:math';
import 'package:intl/intl.dart';
import 'package:shared_preferences/shared_preferences.dart';

class DailyMessageService {
  Future<Map> getDailyMessageAndGif() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var now = DateTime.now();
    var formatter = DateFormat('yyyyMMdd');
    String formattedDate = formatter.format(now);

    String lastPickedDate = prefs.getString('last_picked_date') ?? "";
    Map result = {
      'gif': 'default.gif',
      'message': 'デフォルトメッセージ'
    };

    if (formattedDate != lastPickedDate) {
      // 日付が変わったら新しいメッセージとGIFを選ぶ
      var rng = Random(now.millisecondsSinceEpoch);
      List gifs = [
        'mia1.gif',
        'mia2.gif'
      ]; // assets/imagesからの相対パス(つまりファイル名)を記述
      
      List messages = [
        'ふぅ〜。思考停止',
        'リラックスタイム、最高だね',
        '「なんか面白い話して」って残酷すぎない?',
        '今日は、パンケーキ食べちゃおーっと',
        'この本読み始めたら止まらない、寝不足必至だな',
        'ご注文の料理を持ってきましたニャン',
        '健康診断の結果見るの、怖いー'
      ];

      result['gif'] = gifs[rng.nextInt(gifs.length)];
      result['message'] = messages[rng.nextInt(messages.length)];

      // 選んだメッセージとGIFを保存
      prefs.setString('last_picked_date', formattedDate);
      prefs.setString('daily_gif', result['gif']);
      prefs.setString('daily_message', result['message']);
      
      print('Stored GIF: ${result['gif']}');
      print('Stored Message: ${result['message']}');
    } else {
      // 同じ日なら保存されたメッセージとGIFを使用
      result['gif'] = prefs.getString('daily_gif') ?? 'default.gif';
      result['message'] = prefs.getString('daily_message') ?? 'デフォルトメッセージ';
    }
    
    return result;
  }
}

Call the daily display function on the home screen

Dart
// lib/screens/home/home_tab.dart
import 'daily_message_service.dart';

class _HomeTabState extends ConsumerState {
  // ... (その他の状態変数は省略) ...
  String _dailyGif = 'default.gif';
  String _dailyMessage = 'デフォルトメッセージ';

  @override
  void initState() {
    super.initState();
    _loadDailyMessage();
  }

  void _loadDailyMessage() async {
    DailyMessageService service = DailyMessageService();
    var dailyInfo = await service.getDailyMessageAndGif();
    setState(() {
      _dailyGif = dailyInfo['gif'] ?? 'default.gif';
      _dailyMessage = dailyInfo['message'] ?? 'デフォルトメッセージ';
    });
  }

  @override
  Widget build(BuildContext context) {
    // ... (ビルドメソッドのコードは省略) ...

    return Align(
      alignment: Alignment.center,
      child: Column(
        children: [
          InkWell(
            onTap: () async {
              final message = await grpcService.sayHello('Clocky');
              debugPrint('message: ${message.message}');
            },
            child: Image.asset('assets/images/$_dailyGif',
                width: 250, height: 250),
          ),
          Spacing.h8(),
          Text(
            _dailyMessage,
            style: TextStyle(
              fontSize: 16.0,
              fontWeight: FontWeight.bold,
            ),
          ),
          // ... (その他のウィジェットは省略) ...
        ],
      ),
    );
  }
}

operation check

When the top screen was opened, the default GIF image and text were successfully displayed as shown below.

Next, verify that the GIF animation and phrases change when the date is changed.

For simplicity this time, we will place an “advance one-day” button on the top screen, and when that button is pressed, the date will be changed and the data updated accordingly.

Maintain the current date being simulated using a StateProvider named simulatedDateProvider. When a button is pressed, it updates the value of the StateProvider, calls the _loadDailyMessage method, and uses the DailyMessageService to retrieve data based on the new date.

Override getCurrentDate within the DailyMessageService to allow the service to correctly retrieve data using the new date read from the provider.

Dart
// lib/screens/home/home_tab.dart

final simulatedDateProvider =
    StateProvider<DateTime>((ref) => DateTime.now()); //日付シミュレーション用
    
class _HomeTabState extends ConsumerState<HomeTab> {
  @override
  void initState() {
    super.initState();
    final user = ref.read(userProvider);
    volume = user?.volume ?? 50;
    _loadDailyMessage();
  }
  
  void _advanceDay() {
    // 現在のシミュレートされた日付を1日進める
    ref.read(simulatedDateProvider.notifier).update((state) => state.add(Duration(days: 1)));

    // 新しい日付でデータを再取得する
    _loadDailyMessage();
  }

  void _loadDailyMessage() {
    DailyMessageService service = DailyMessageService();
    var date = ref.read(simulatedDateProvider);
    service.getCurrentDate = () => date;  // 日付をServiceに渡す
    service.getDailyMessageAndGif().then((result) {
      // 結果に基づいてUIを更新する
      setState(() {
        // 画像やメッセージを更新
      });
    });
  }

  Widget build(BuildContext context) {
    final simulatedDate = ref.watch(simulatedDateProvider);

    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              onPressed: () {
                // ボタンが押された時の処理
                _advanceDay();
              },
              child: Text('1日進める')
            ),
            // その他のウィジェット...
          ],
        ),
      ),
    );
  }

  
}
Dart
flutter: Stored GIF: mia1.gif
flutter: Stored Message: 健康診断の結果見るの、怖いー
flutter: Stored GIF: mia1.gif
flutter: Stored Message: 健康診断の結果見るの、怖いー
flutter: Stored GIF: mia1.gif
flutter: Stored Message: 朝の体重計、いつもドキドキ
flutter: Stored GIF: mia2.gif
flutter: Stored Message: こわい夢見ちゃった〜

On this screen, by clicking the “Advance by 1 day” button under the volume control slider, the local storage shows the GIF animation and text after the change, which is also reflected in the UI.

I think this renovation has made the home screen much more interactive.

コメント

タイトルとURLをコピーしました