ソースコード source code

下記アプリの主要なソースコードを公開しています。アプリ開発の参考になれば幸いです。

画像等が別途必要ですので下記情報のみでアプリが完成するものではありません。 アプリは少しずつ機能拡張していますのでストア公開されているアプリと内容が異なる場合があります。 コードはコピーして自由にお使いいただけます。ただし著作権は放棄しておりませんので全部の再掲載はご遠慮ください。部分的に再掲載したり、改変して再掲載するのは構いません。 自身のアプリ作成の参考として個人使用・商用問わず自由にお使いいただけます。 コード記述のお手本を示すものではありません。ミニアプリですので変数名などさほど気遣いしていない部分も有りますし間違いも有るかと思いますので参考程度にお考え下さい。 他の賢者の皆様が公開されているコードを参考にした箇所も含まれます。Flutter開発の熟練者が書いたコードではありません。 エンジニア向け技術情報共有サービスではありませんので説明は省いています。 GitHubなどへの公開は予定しておりません。

下記コードの最終ビルド日: 2022-06-27

pubspec.yaml

name: ramentimer
description: RamenTimer

# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.17.3 <3.0.0"

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.0.15
  just_audio: ^0.9.25
  flutter_svg: ^1.1.0
  google_mobile_ads: ^1.3.0
  flutter_localizations:    #多言語ライブラリの本体
    sdk: flutter
  intl: ^0.17.0   #多言語やフォーマッタなどの関連ライブラリ
  package_info_plus: ^1.4.2

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.5

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_launcher_icons: ^0.9.3
  flutter_native_splash: ^2.2.3+1

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0

flutter_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon/icon.png"
  adaptive_icon_background: "assets/icon/icon_back.png"
  adaptive_icon_foreground: "assets/icon/icon_fore.png"

flutter_native_splash:
  color: '#ffcd40'
  image: 'assets/image/splash.png'
  color_dark: '#ffcd40'
  image_dark: 'assets/image/splash.png'
  fullscreen: true
  android_12:
    icon_background_color: '#ffcd40'
    image: 'assets/image/splash.png'
    icon_background_color_dark: '#ffcd40'
    image_dark: 'assets/image/splash.png'


# 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 packages.
flutter:
  assets:
    - assets/sound/
    - assets/icon/
    - assets/image/
  generate: true    #自動生成フラグの有効化

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, 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 from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

lib/main.dart

///
/// instant cup ramen timer
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2022-06-27
///
///

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:just_audio/just_audio.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:async';
import 'dart:io';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp, //縦固定
  ]);
  MobileAds.instance.initialize();
  runApp(const MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({super.key});
  @override
  State<MainApp> createState() => _MainAppState();
}

class Pref {
  static const String keyTimerCounter = "keyTimerCounter";
  static const String keySound = "keySound";
  static const String keyLanguage = "keyLanguage";
}

class LanguageChoice {
  final Map<String,String> langMap = {
    'en': 'English',
    'bg': 'български език',
    'cs': 'Čeština',
    'da': 'dansk',
    'de': 'Deutsch',
    'el': 'Ελληνικά',
    'es': 'Español',
    'et': 'eesti keel',
    'fi': 'Suomen kieli',
    'fr': 'Français',
    'hu': 'magyar nyelv',
    'it': 'Italiano',
    'ja': '日本語',
    'lt': 'lietuvių kalba',
    'lv': 'Latviešu',
    'nl': 'Nederlands',
    'pl': 'Polski',
    'pt': 'Português',
    'ro': 'limba română',
    'ru': 'русский',
    'sk': 'Slovenčina',
    'sv': 'svenska',
    'th': 'ภาษาไทย',
    'zh': '中文',
  };
  List<String> languageList() {
    List<String> languages = [];
    langMap.forEach((key, value) {
      languages.add(key);
    });
    return languages;
  }
  String getLanguageName(String str) {
    if (langMap.containsKey(str)) {
      return langMap[str]!;
    }
    return '';
  }
  List<Locale> localeList() {
    List<Locale> locales = [];
    langMap.forEach((key, value) {
      locales.add(Locale(key));
    });
    return locales;
  }
}

class ColorDesign {
  static const Color appBackground = Color.fromARGB(255, 87,81,32);
  static const Color appHeader = Color.fromARGB(255, 185,165,54);
  static const Color appHeaderLine = Color.fromARGB(255, 206,181,65);
  static const Color appHeaderText = Color.fromARGB(255, 255, 255, 255);
  static const Color appBody = Color.fromARGB(255, 255, 255, 255);
  static const Color canvasClose = Color.fromARGB(255, 255, 255, 255);
  static const Color canvasOpen = Color.fromARGB(255,243,51,41);
}

class AudioPlay {
  static const int soundOff = 1;
  static const int soundOn = 2;
  final String _audioStart = 'assets/sound/start.mp3';
  final String _audioStop = 'assets/sound/stop.mp3';
  final String _audioPushUp = 'assets/sound/push1.mp3';
  final String _audioPushDown = 'assets/sound/push2.mp3';
  final String _audioComplete = 'assets/sound/niwatori.mp3';
  static final List<AudioPlayer> _audioPlayerStart = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  static final List<AudioPlayer> _audioPlayerStop = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  static final List<AudioPlayer> _audioPlayerPushUp = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  static final List<AudioPlayer> _audioPlayerPushDown = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  static final List<AudioPlayer> _audioPlayerComplete = [
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
    AudioPlayer(),
  ];
  int _sound = soundOn;
  int _audioPlayerStartPtr = 0;
  int _audioPlayerStopPtr = 0;
  int _audioPlayerPushUpPtr = 0;
  int _audioPlayerPushDownPtr = 0;
  int _audioPlayerCompletePtr = 0;
  AudioPlay() { //constructor
    constructor();
  }
  void constructor() async {
    for (int i = 0; i < _audioPlayerStart.length; i++) {
      await _audioPlayerStart[i].setAsset(_audioStart);
    }
    for (int i = 0; i < _audioPlayerStop.length; i++) {
      await _audioPlayerStop[i].setAsset(_audioStop);
    }
    for (int i = 0; i < _audioPlayerPushUp.length; i++) {
      await _audioPlayerPushUp[i].setAsset(_audioPushUp);
    }
    for (int i = 0; i < _audioPlayerPushDown.length; i++) {
      await _audioPlayerPushDown[i].setAsset(_audioPushDown);
    }
    for (int i = 0; i < _audioPlayerComplete.length; i++) {
      await _audioPlayerComplete[i].setAsset(_audioComplete);
    }
  }
  void dispose() {
    for (int i = 0; i < _audioPlayerStart.length; i++) {
      _audioPlayerStart[i].dispose();
    }
    for (int i = 0; i < _audioPlayerStop.length; i++) {
      _audioPlayerStop[i].dispose();
    }
    for (int i = 0; i < _audioPlayerPushUp.length; i++) {
      _audioPlayerPushUp[i].dispose();
    }
    for (int i = 0; i < _audioPlayerPushDown.length; i++) {
      _audioPlayerPushDown[i].dispose();
    }
    for (int i = 0; i < _audioPlayerComplete.length; i++) {
      _audioPlayerComplete[i].dispose();
    }
  }
  void setSoundMode(int soundMode) {
    _sound = soundMode;
  }
  int getSoundMode() {
    return _sound;
  }
  String getSoundModeStr(dynamic context) {
    if (_sound == soundOff) {
      return AppLocalizations.of(context)!.off;
    } else if (_sound == soundOn) {
      return AppLocalizations.of(context)!.on;
    }
    return '';
  }
  void playSoundStart() async {
    if (_sound != soundOn) {
      return;
    }
    _audioPlayerStartPtr += 1;
    if (_audioPlayerStartPtr >= _audioPlayerStart.length) {
      _audioPlayerStartPtr = 0;
    }
    await _audioPlayerStart[_audioPlayerStartPtr].pause();
    await _audioPlayerStart[_audioPlayerStartPtr].seek(Duration.zero);
    await _audioPlayerStart[_audioPlayerStartPtr].play();
  }
  void playSoundStop() async {
    if (_sound != soundOn) {
      return;
    }
    _audioPlayerStopPtr += 1;
    if (_audioPlayerStopPtr >= _audioPlayerStop.length) {
      _audioPlayerStopPtr = 0;
    }
    await _audioPlayerStop[_audioPlayerStopPtr].pause();
    await _audioPlayerStop[_audioPlayerStopPtr].seek(Duration.zero);
    await _audioPlayerStop[_audioPlayerStopPtr].play();
  }
  void playSoundPushUp() async {
    if (_sound != soundOn) {
      return;
    }
    _audioPlayerPushUpPtr += 1;
    if (_audioPlayerPushUpPtr >= _audioPlayerPushUp.length) {
      _audioPlayerPushUpPtr = 0;
    }
    await _audioPlayerPushUp[_audioPlayerPushUpPtr].pause();
    await _audioPlayerPushUp[_audioPlayerPushUpPtr].seek(Duration.zero);
    await _audioPlayerPushUp[_audioPlayerPushUpPtr].play();
  }
  void playSoundPushDown() async {
    if (_sound != soundOn) {
      return;
    }
    _audioPlayerPushDownPtr += 1;
    if (_audioPlayerPushDownPtr >= _audioPlayerPushDown.length) {
      _audioPlayerPushDownPtr = 0;
    }
    await _audioPlayerPushDown[_audioPlayerPushDownPtr].pause();
    await _audioPlayerPushDown[_audioPlayerPushDownPtr].seek(Duration.zero);
    await _audioPlayerPushDown[_audioPlayerPushDownPtr].play();
  }
  void playSoundComplete() async {
    if (_sound != soundOn) {
      return;
    }
    _audioPlayerCompletePtr += 1;
    if (_audioPlayerCompletePtr >= _audioPlayerComplete.length) {
      _audioPlayerCompletePtr = 0;
    }
    await _audioPlayerComplete[_audioPlayerCompletePtr].pause();
    await _audioPlayerComplete[_audioPlayerCompletePtr].seek(Duration.zero);
    await _audioPlayerComplete[_audioPlayerCompletePtr].play();
  }
}

class AppCondition {
  static const int ready = 0;
  static const int complete = 1;
  int _gameCondition = 0;
  AppCondition() { //constructor
    _gameCondition = ready;
  }
  int getGameCondition() {
    return _gameCondition;
  }
  void setGameCondition(int cond) {
    _gameCondition = cond;
  }
  Color stageColor() {
    if (_gameCondition == ready) {
      return ColorDesign.canvasClose;
    } else if (_gameCondition == complete) {
      return ColorDesign.canvasOpen;
    }
    return ColorDesign.appBody;
  }
  Color bgColor() {
    if (_gameCondition == ready) {
      return ColorDesign.appBody;
    } else if (_gameCondition == complete) {
      return ColorDesign.canvasOpen;
    }
    return ColorDesign.appBody;
  }
}

class RamenImage {
  static const String close = 'assets/image/cupramen_close.svg';
  static const String open = 'assets/image/cupramen_open.svg';
  String getImageStr(bool openClose) {
    if (openClose) {
      return open;
    }
    return close;
  }
}

class RamenTickTimer {
  int _timerCounter = 180;
  bool _isTimerActive = true;
  void stop() {
    _isTimerActive = false;
  }
  void resume() {
    _isTimerActive = true;
  }
  bool getIsActive() {
    return _isTimerActive;
  }
  void setTimerCounter(int sec) {
    _timerCounter = sec;
  }
  int getTimerCounter() {
    return _timerCounter;
  }
  void addTimerCounter(int sec) {
    _timerCounter += sec;
  }
  String getTimeStr() {
    if (_timerCounter> 0) {
      int minute = (_timerCounter / 60).floor();
      int second = (_timerCounter % 60);
      return '$minute:${second.toString().padLeft(2,'0')}';
    } else {
      final int counter = _timerCounter * -1;
      int minute = (counter / 60).floor();
      int second = (counter % 60);
      return '-$minute:${second.toString().padLeft(2,'0')}';
    }
  }
}

class AdMob {
  late BannerAd _adMobBanner;
  bool _isAdMob = false;
  AdMob() { //constructor
    String adBannerUnitId = '';
    if (!kIsWeb && Platform.isAndroid) {
      adBannerUnitId = 'ca-app-pub-0000000000000000/0000000000';
      _isAdMob = true;
    } else if (!kIsWeb && Platform.isIOS) {
      adBannerUnitId = 'ca-app-pub-0000000000000000/0000000000';
      _isAdMob = true;
    }
    if (_isAdMob) {
      _adMobBanner = BannerAd(
        adUnitId: adBannerUnitId,
        size: AdSize.banner,
        request: const AdRequest(),
        listener: const BannerAdListener(),
      );
    }
  }
  void load() {
    if (_isAdMob) {
      _adMobBanner.load();
    }
  }
  Widget getAdBannerWidget() {
    if (_isAdMob) {
      return Container(
        alignment: Alignment.center,
        width: _adMobBanner.size.width.toDouble(),
        height: _adMobBanner.size.height.toDouble(),
        child: AdWidget(ad: _adMobBanner),
      );
    } else {
      return Container(height: 150);
    }
  }
  double getAdBannerHeight() {
    if (_isAdMob) {
      return _adMobBanner.size.height.toDouble();
    } else {
      return 150;  //for web
    }
  }
}

//-------------------------------------------------

class _MainAppState extends State<MainApp> {
  Locale locale = AppLocalizations.supportedLocales.first;
  @override
  Widget build(BuildContext context) {
    final LanguageChoice languageChoice = LanguageChoice();
    final List<Locale> localeList = languageChoice.localeList();
    return MaterialApp(
      debugShowCheckedModeBanner: false,  //debug文字非表示
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: localeList,
      locale: locale,
      home: const MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({Key? key}) : super(key: key);
  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
  final AppCondition appCondition = AppCondition();
  final AdMob adMob = AdMob();
  final AudioPlay audioPlay = AudioPlay();
  final RamenImage ramenImage = RamenImage();
  final RamenTickTimer ramenTickTimer = RamenTickTimer();
  String _version = '';
  @override
  void initState() {
    super.initState();
    _getVersion();
    adMob.load();
    _loadTimerCounter();
    _ramenTickTimerStart();
    _loadSound();
    _loadLanguage();
  }
  @override
  void dispose() {
    audioPlay.dispose();
    super.dispose();
  }
  Future _getVersion() async {
    PackageInfo packageInfo = await PackageInfo.fromPlatform();
    setState(() {
      _version = packageInfo.version;
    });
  }
  void _ramenTickTimerStart() {
    Timer.periodic(
      const Duration(seconds: 1), (Timer timer) {
        if (ramenTickTimer.getIsActive()) {
          ramenTickTimer.addTimerCounter(-1);
          final int timerCounter = ramenTickTimer.getTimerCounter();
          if (timerCounter > 0) {
            setState(() {
              appCondition.setGameCondition(AppCondition.ready);
            });
          } else {
            if (timerCounter == 0) {
              audioPlay.playSoundComplete();
            }
            setState(() {
              appCondition.setGameCondition(AppCondition.complete);
            });
          }
        }
      },
    );
  }
  void _loadLanguage() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String lang = prefs.getString(Pref.keyLanguage) ?? 'en';
    if (!mounted) {
      return;
    }
    context.findAncestorStateOfType<_MainAppState>()!
      ..locale = Locale(lang)
      ..setState(() {});
  }
  void _saveLanguage(String lang) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(Pref.keyLanguage, lang);
  }
  void _loadSound() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    audioPlay.setSoundMode(prefs.getInt(Pref.keySound) ?? AudioPlay.soundOn);
  }
  void _saveSound() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt(Pref.keySound, audioPlay.getSoundMode());
  }
  void _loadTimerCounter() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      ramenTickTimer.setTimerCounter(prefs.getInt(Pref.keyTimerCounter) ?? 180);
    });
  }
  void _saveTimerCounter() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt(Pref.keyTimerCounter, ramenTickTimer.getTimerCounter());
  }
  void _onClickMinuteButton(int minute) {
    audioPlay.playSoundStart();
    setState(() {
      ramenTickTimer.setTimerCounter(minute * 60);
    });
    _saveTimerCounter();
  }
  void _onClickSecondButton(int second) {
    if (second > 0) {
      audioPlay.playSoundPushUp();
    } else {
      audioPlay.playSoundPushDown();
    }
    setState(() {
      ramenTickTimer.addTimerCounter(second);
    });
  }
  void _onClickStopButton() {
    audioPlay.playSoundStop();
    ramenTickTimer.stop();
  }
  void _onClickStartButton() {
    audioPlay.playSoundStart();
    ramenTickTimer.resume();
  }

  //-----------------------------------------------

  Widget _startOneButton() {
    return Expanded(
      child: ElevatedButton(
        onPressed: () {
          _onClickMinuteButton(1);
        },
        style: ElevatedButton.styleFrom(
          elevation: 0,
          primary: ColorDesign.appHeader,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
          side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
          padding: const EdgeInsets.symmetric(
            horizontal: 0,
            vertical: 8,
          ),
        ),
        child: Text(AppLocalizations.of(context)!.min1, style: const TextStyle(color: ColorDesign.appHeaderText)),
      )
    );
  }
  Widget _startTwoButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickMinuteButton(2);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.min2, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _startThreeButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickMinuteButton(3);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.min3, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _startFourButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickMinuteButton(4);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.min4, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _startFiveButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickMinuteButton(5);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.min5, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _startSixButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickMinuteButton(6);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.min6, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _minusButton() {
    return Expanded(
      child: ElevatedButton(
        onPressed: () {
          _onClickSecondButton(-10);
        },
        style: ElevatedButton.styleFrom(
          elevation: 0,
          primary: ColorDesign.appHeader,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
          side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
          padding: const EdgeInsets.symmetric(
            horizontal: 0,
            vertical: 8,
          ),
        ),
        child: Text(AppLocalizations.of(context)!.minus10sec, style: const TextStyle(color: ColorDesign.appHeaderText)),
      )
    );
  }
  Widget _plusButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: () {
            _onClickSecondButton(10);
          },
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.plus10sec, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _stopButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: _onClickStopButton,
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.stop, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _startButton() {
    return Expanded(
        child: ElevatedButton(
          onPressed: _onClickStartButton,
          style: ElevatedButton.styleFrom(
            elevation: 0,
            primary: ColorDesign.appHeader,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)),
            side: const BorderSide(width: 0.5, color: ColorDesign.appHeaderLine),
            padding: const EdgeInsets.symmetric(
              horizontal: 0,
              vertical: 8,
            ),
          ),
          child: Text(AppLocalizations.of(context)!.resume, style: const TextStyle(color: ColorDesign.appHeaderText)),
        )
    );
  }
  Widget _dialogSound() {
    return SimpleDialog(
      title: Text('${AppLocalizations.of(context)!.sound}: ${audioPlay.getSoundModeStr(context)}'),
      children: <Widget>[
        SimpleDialogOption(
            child: Text(AppLocalizations.of(context)!.off),
            onPressed: () {
              setState(() {
                audioPlay.setSoundMode(AudioPlay.soundOff);
              });
              _saveSound();
              Navigator.pop(context);
            }
        ),
        SimpleDialogOption(
            child: Text(AppLocalizations.of(context)!.on),
            onPressed: () {
              setState(() {
                audioPlay.setSoundMode(AudioPlay.soundOn);
              });
              _saveSound();
              Navigator.pop(context);
            }
        ),
      ],
    );
  }
  Widget _dialogLanguage() {
    final LanguageChoice languageChoice = LanguageChoice();
    final List<String> langList = languageChoice.languageList();
    return SimpleDialog(
      title: Text(AppLocalizations.of(context)!.language),
      children: List.generate(langList.length,(index) {
        return SimpleDialogOption(
            child: Row(
                children:[
                  SizedBox(
                      width: 25,
                      child: Text(langList[index])
                  ),
                  Text('- ${languageChoice.getLanguageName(langList[index])}')
                ]
            ),
            onPressed: () {
              _saveLanguage(langList[index]);
              context.findAncestorStateOfType<_MainAppState>()!
                ..locale = Locale(langList[index])
                ..setState((){});
              Navigator.pop(context);
            }
        );
      }),
    );
  }
  Widget _hamburgerMenu() {
    return Drawer(
      child: ListView(
        padding: EdgeInsets.zero,
        children: [
          SizedBox(
              //height: 190,
              child: DrawerHeader(
                decoration: const BoxDecoration(color: Colors.blue),
                child: Column(children:[
                  Row(children:[
                    SvgPicture.asset(RamenImage.close, width: 80, height: 96),
                    const SizedBox(width: 8),
                    Flexible(child: Text(AppLocalizations.of(context)!.appName,style: const TextStyle(color: Colors.white))),
                  ]),
                  Text(AppLocalizations.of(context)!.usage,style: const TextStyle(fontSize: 11,color: Colors.white)),
                ]),
              )
          ),
          ListTile(
            leading: const CircleAvatar(
              child: Icon(
                Icons.audiotrack,
              ),
            ),
            title: Text('${AppLocalizations.of(context)!.sound}: ${audioPlay.getSoundModeStr(context)}'),
            trailing: const Icon(
              Icons.arrow_circle_right_outlined,
            ),
            onTap: () {
              showDialog(
                  context: context,
                  barrierDismissible: true,
                  builder: (_) {
                    return _dialogSound();
                  }
              );
            },
          ),
          Container(
            padding: const EdgeInsets.only(left: 15,right: 15,bottom: 20),
            child: Text(AppLocalizations.of(context)!.sound1,style: const TextStyle(fontSize: 11,color: Colors.black54)),
          ),
          ListTile(
            leading: const CircleAvatar(
              child: Icon(
                Icons.language,
              ),
            ),
            title: Text(AppLocalizations.of(context)!.language),
            trailing: const Icon(
              Icons.arrow_circle_right_outlined,
            ),
            onTap: () {
              showDialog(
                  context: context,
                  barrierDismissible: true,
                  builder: (_) {
                    return _dialogLanguage();
                  }
              );
            },
          ),
          Container(
            padding: const EdgeInsets.only(left: 15,right: 15,top: 50),
            child: Text('v $_version',style: const TextStyle(fontSize: 11,color: Colors.black45)),
          ),
        ],
      ),
    );
  }
  Widget _settingButton() {
    return IconButton(
      icon: const Icon(Icons.menu,color: ColorDesign.appHeaderText),
      tooltip: AppLocalizations.of(context)!.setting,
      onPressed: () {
        _key.currentState?.openDrawer();
      },
    );
  }
  Widget _menuBar() {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8.0),
      decoration: const BoxDecoration(color: ColorDesign.appHeader),
      child: Column(children: <Widget> [
        Row(children: <Widget> [
          _startOneButton(),
          const SizedBox(width: 8),
          _startTwoButton(),
          const SizedBox(width: 8),
          _startThreeButton(),
          const SizedBox(width: 8),
          _startFourButton(),
          const SizedBox(width: 8),
          _startFiveButton(),
          const SizedBox(width: 8),
          _startSixButton(),
        ]),
        Row(children: <Widget> [
          _minusButton(),
          const SizedBox(width: 8),
          _plusButton(),
          const SizedBox(width: 8),
          _stopButton(),
          const SizedBox(width: 8),
          _startButton(),
          const SizedBox(width: 8),
          _settingButton(),
        ]),
      ]),
    );
  }
  Widget _boxCanvas() {
    final String ramenImageStr = (ramenTickTimer.getTimerCounter() <= 0) ? ramenImage.getImageStr(true) : ramenImage.getImageStr(false);
    final String timeStr = ramenTickTimer.getTimeStr();
    return Stack(
      children: [
        SvgPicture.asset(
          ramenImageStr,
          semanticsLabel: 'ramen',
          width: double.infinity,
          height: double.infinity,
        ),
        AspectRatio(
          aspectRatio: 1 / 1,
          child: Container(
            alignment: Alignment.center,
            child: FractionallySizedBox(
              widthFactor: 0.26,
              child: FittedBox(
                fit: BoxFit.fitWidth,
                child: Text(timeStr, style: const TextStyle(fontSize: 100, fontWeight: FontWeight.w900, color: Colors.yellowAccent)),
              ),
            ),
          ),
        ),
      ]
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _key,
      appBar: null,
      drawer: _hamburgerMenu(),
      backgroundColor: ColorDesign.appBackground,
      body: SafeArea(
        child: Center(
          child: Column(
            children: [
              _menuBar(),
              Expanded(
                child: Container(
                  color: appCondition.bgColor(),
                  padding: const EdgeInsets.only(top: 0, left: 8, right: 8, bottom: 10),
                  child: Center(
                    child: AspectRatio(
                      aspectRatio: 1 / 1.2,
                      child: Container(
                        width: double.infinity,
                        height: double.infinity,
                        color: appCondition.stageColor(),
                        child: _boxCanvas(),
                      ),
                    ),
                  ),
                ),
              ),
              Container(
                width: double.infinity,
                color: appCondition.bgColor(),
                child: adMob.getAdBannerWidget(),
              ),
            ]
          ),
        ),
      ),
    );
  }
}