pubspec.yaml
name: wattconversion
description: "WattConversion"
# 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 is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.1.0+7
environment:
sdk: ^3.9.2
# 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
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
package_info_plus: ^9.0.0
shared_preferences: ^2.0.17
flutter_localizations: #多言語ライブラリの本体 # .arbファイルを更新したら flutter gen-l10n
sdk: flutter
intl: ^0.20.2 #多言語やフォーマッタなどの関連ライブラリ
google_mobile_ads: ^6.0.0
just_audio: ^0.10.4
dev_dependencies:
flutter_test:
sdk: flutter
flutter_launcher_icons: ^0.14.4 #flutter pub run flutter_launcher_icons
flutter_native_splash: ^2.3.6 #flutter pub run flutter_native_splash:create
# 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: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
flutter_launcher_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: '#3dbdd0'
image: 'assets/image/splash.png'
color_dark: '#3dbdd0'
image_dark: 'assets/image/splash.png'
fullscreen: true
android_12:
icon_background_color: '#3dbdd0'
image: 'assets/image/splash.png'
icon_background_color_dark: '#3dbdd0'
image_dark: 'assets/image/splash.png'
# The following section is specific to Flutter packages.
flutter:
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
assets:
- assets/image/
- assets/sound/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/to/asset-from-package
# 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/to/font-from-package
lib/ad_banner_widget.dart
import 'package:flutter/cupertino.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:wattconversion/ad_manager.dart';
class AdBannerWidget extends StatefulWidget {
final AdManager adManager;
const AdBannerWidget({super.key, required this.adManager});
@override
State<AdBannerWidget> createState() => _AdBannerWidgetState();
}
class _AdBannerWidgetState extends State<AdBannerWidget> {
int _lastBannerWidthDp = 0;
bool _isAdLoaded = false;
bool _isLoading = false;
@override
Widget build(BuildContext context) {
return SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
final int width = constraints.maxWidth.isFinite
? constraints.maxWidth.truncate()
: MediaQuery.of(context).size.width.truncate();
final bannerAd = widget.adManager.bannerAd;
if (width > 0) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
final bannerAd = widget.adManager.bannerAd;
final bool widthChanged = _lastBannerWidthDp != width;
final bool sizeMismatch =
bannerAd == null || bannerAd.size.width != width;
if ((widthChanged || !_isAdLoaded || sizeMismatch) &&
!_isLoading) {
_lastBannerWidthDp = width;
setState(() {
_isAdLoaded = false;
_isLoading = true;
});
widget.adManager.loadAdaptiveBannerAd(width, () {
if (mounted) {
setState(() {
_isAdLoaded = true;
_isLoading = false;
});
}
});
}
}
});
}
if (_isAdLoaded && bannerAd != null) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: bannerAd.size.width.toDouble(),
height: bannerAd.size.height.toDouble(),
child: AdWidget(ad: bannerAd),
),
],
)
]
);
} else {
return const SizedBox.shrink();
}
},
),
);
}
}
lib/ad_manager.dart
import 'dart:async';
import 'dart:io' show Platform;
import 'dart:ui';
import 'package:google_mobile_ads/google_mobile_ads.dart';
class AdManager {
// Test IDs
// static const String _androidAdUnitId = "ca-app-pub-3940256099942544/6300978111";
// static const String _iosAdUnitId = "ca-app-pub-3940256099942544/2934735716";
// Production IDs
static const String _androidAdUnitId = "ca-app-pub-0/0";
static const String _iosAdUnitId = "ca-app-pub-0/0";
static String get _adUnitId =>
Platform.isIOS ? _iosAdUnitId : _androidAdUnitId;
BannerAd? _bannerAd;
int _lastWidthPx = 0;
VoidCallback? _onLoadedCb;
Timer? _retryTimer;
int _retryAttempt = 0;
BannerAd? get bannerAd => _bannerAd;
Future<void> loadAdaptiveBannerAd(
int widthPx,
VoidCallback onAdLoaded,
) async {
_onLoadedCb = onAdLoaded;
_lastWidthPx = widthPx;
_retryAttempt = 0;
_retryTimer?.cancel();
_startLoad(widthPx);
}
Future<void> _startLoad(int widthPx) async {
_bannerAd?.dispose();
AnchoredAdaptiveBannerAdSize? adaptiveSize;
try {
adaptiveSize =
await AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(
widthPx,
);
} catch (_) {
adaptiveSize = null;
}
final AdSize size = adaptiveSize ?? AdSize.fullBanner;
_bannerAd = BannerAd(
adUnitId: _adUnitId,
request: const AdRequest(),
size: size,
listener: BannerAdListener(
onAdLoaded: (ad) {
_retryTimer?.cancel();
_retryAttempt = 0;
final cb = _onLoadedCb;
if (cb != null) {
cb();
}
},
onAdFailedToLoad: (ad, err) {
ad.dispose();
_scheduleRetry();
},
),
)..load();
}
void _scheduleRetry() {
_retryTimer?.cancel();
// Exponential backoff: 3s, 6s, 12s, max 30s
_retryAttempt = (_retryAttempt + 1).clamp(1, 5);
final seconds = _retryAttempt >= 4 ? 30 : (3 << (_retryAttempt - 1));
_retryTimer = Timer(Duration(seconds: seconds), () {
_startLoad(_lastWidthPx > 0 ? _lastWidthPx : 320);
});
}
void dispose() {
_bannerAd?.dispose();
_retryTimer?.cancel();
}
}
lib/audio_play.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-01-27
///
library;
import 'package:just_audio/just_audio.dart';
import 'package:wattconversion/const_value.dart';
class AudioPlay {
//音を重ねて連続再生できるようにインスタンスを用意しておき、順繰りに使う。
static final List<AudioPlayer> _player01 = [
AudioPlayer(),
AudioPlayer(),
AudioPlayer(),
AudioPlayer(),
AudioPlayer(),
AudioPlayer(), //6個
];
int _player01Ptr = 0;
double _soundVolume = 0.0;
//constructor
AudioPlay() {
constructor();
}
void constructor() async {
for (int i = 0; i < _player01.length; i++) {
await _player01[i].setVolume(0);
await _player01[i].setAsset(ConstValue.audioHiyoko[i]);
}
playZero();
}
void dispose() {
for (int i = 0; i < _player01.length; i++) {
_player01[i].dispose();
}
}
//getter
double get soundVolume {
return _soundVolume;
}
//setter
set soundVolume(double vol) {
_soundVolume = vol;
}
//最初に音が鳴らないのを回避する方法
void playZero() async {
AudioPlayer ap = AudioPlayer();
await ap.setAsset(ConstValue.audioZero);
await ap.load();
await ap.play();
}
//
void play01() async {
if (_soundVolume == 0) {
return;
}
_player01Ptr += 1;
if (_player01Ptr >= _player01.length) {
_player01Ptr = 0;
}
await _player01[_player01Ptr].setVolume(_soundVolume);
await _player01[_player01Ptr].pause();
await _player01[_player01Ptr].seek(Duration.zero);
await _player01[_player01Ptr].play();
}
}
lib/const_value.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-15
///
library;
import 'package:flutter/material.dart';
class ConstValue {
//pref
static const String prefLanguageCode = 'languageCode';
static const String prefShowBackImage = 'showBackImage';
static const String prefSoundVolume = 'soundVolume';
static const String prefWattFrom = 'wattFrom';
static const String prefWattTo = 'wattTo';
static const String prefMinute = 'minute';
static const String prefSecond = 'second';
static const String prefThemeNumber = 'themeNumber';
static const List<String> imageBacks = [
'assets/image/kitchen001.webp',
'assets/image/kitchen002.webp',
'assets/image/kitchen003.webp',
'assets/image/kitchen004.webp',
'assets/image/kitchen005.webp',
'assets/image/kitchen006.webp',
'assets/image/kitchen007.webp',
'assets/image/kitchen008.webp',
'assets/image/kitchen009.webp',
'assets/image/kitchen010.webp',
'assets/image/kitchen011.webp',
'assets/image/kitchen012.webp',
'assets/image/kitchen013.webp',
'assets/image/kitchen014.webp',
'assets/image/kitchen015.webp',
'assets/image/kitchen016.webp',
'assets/image/kitchen017.webp',
'assets/image/kitchen018.webp',
'assets/image/kitchen019.webp',
'assets/image/kitchen020.webp',
'assets/image/kitchen021.webp',
'assets/image/kitchen022.webp',
'assets/image/kitchen023.webp',
'assets/image/kitchen024.webp',
'assets/image/kitchen025.webp',
'assets/image/kitchen026.webp',
'assets/image/kitchen027.webp',
'assets/image/kitchen028.webp',
'assets/image/kitchen029.webp',
'assets/image/kitchen030.webp',
'assets/image/kitchen031.webp',
'assets/image/kitchen032.webp',
'assets/image/kitchen033.webp',
'assets/image/kitchen034.webp',
'assets/image/kitchen035.webp',
'assets/image/kitchen036.webp',
'assets/image/kitchen037.webp',
'assets/image/kitchen038.webp',
'assets/image/kitchen039.webp',
'assets/image/kitchen040.webp',
'assets/image/kitchen041.webp',
'assets/image/kitchen042.webp',
'assets/image/kitchen043.webp',
'assets/image/kitchen044.webp',
'assets/image/kitchen045.webp',
'assets/image/kitchen046.webp',
'assets/image/kitchen047.webp',
'assets/image/kitchen048.webp',
'assets/image/kitchen049.webp',
'assets/image/kitchen050.webp',
'assets/image/kitchen051.webp',
'assets/image/kitchen052.webp',
'assets/image/kitchen053.webp',
'assets/image/kitchen054.webp',
'assets/image/kitchen055.webp',
'assets/image/kitchen056.webp',
'assets/image/kitchen057.webp',
'assets/image/kitchen058.webp',
'assets/image/kitchen059.webp',
'assets/image/kitchen060.webp',
'assets/image/kitchen061.webp',
'assets/image/kitchen062.webp',
'assets/image/kitchen063.webp',
'assets/image/kitchen064.webp',
'assets/image/kitchen065.webp',
'assets/image/kitchen066.webp',
'assets/image/kitchen067.webp',
'assets/image/kitchen068.webp',
'assets/image/kitchen069.webp',
'assets/image/kitchen070.webp',
'assets/image/kitchen071.webp',
'assets/image/kitchen072.webp',
'assets/image/kitchen073.webp',
'assets/image/kitchen074.webp',
'assets/image/kitchen075.webp',
'assets/image/kitchen076.webp',
'assets/image/kitchen077.webp',
'assets/image/kitchen078.webp',
'assets/image/kitchen079.webp',
'assets/image/kitchen080.webp',
'assets/image/kitchen081.webp',
'assets/image/kitchen082.webp',
'assets/image/kitchen083.webp',
'assets/image/kitchen084.webp',
'assets/image/kitchen085.webp',
'assets/image/kitchen086.webp',
'assets/image/kitchen087.webp',
'assets/image/kitchen088.webp',
'assets/image/kitchen089.webp',
'assets/image/kitchen090.webp',
'assets/image/kitchen091.webp',
'assets/image/kitchen092.webp',
'assets/image/kitchen093.webp',
'assets/image/kitchen094.webp',
'assets/image/kitchen095.webp',
'assets/image/kitchen096.webp',
'assets/image/kitchen097.webp',
'assets/image/kitchen098.webp',
'assets/image/kitchen099.webp',
'assets/image/kitchen100.webp',
'assets/image/kitchen101.webp',
'assets/image/kitchen102.webp',
'assets/image/kitchen103.webp',
'assets/image/kitchen104.webp',
'assets/image/kitchen105.webp',
'assets/image/kitchen106.webp',
'assets/image/kitchen107.webp',
'assets/image/kitchen108.webp',
'assets/image/kitchen109.webp',
'assets/image/kitchen110.webp',
'assets/image/kitchen111.webp',
'assets/image/kitchen112.webp',
'assets/image/kitchen113.webp',
'assets/image/kitchen114.webp',
'assets/image/kitchen115.webp',
'assets/image/kitchen116.webp',
'assets/image/kitchen117.webp',
'assets/image/kitchen118.webp',
'assets/image/kitchen119.webp',
'assets/image/kitchen120.webp',
'assets/image/kitchen121.webp',
'assets/image/kitchen122.webp',
'assets/image/kitchen123.webp',
'assets/image/kitchen124.webp',
'assets/image/kitchen125.webp',
'assets/image/kitchen126.webp',
'assets/image/kitchen127.webp',
'assets/image/kitchen128.webp',
'assets/image/kitchen129.webp',
'assets/image/kitchen130.webp',
'assets/image/kitchen131.webp',
'assets/image/kitchen132.webp',
'assets/image/kitchen133.webp',
'assets/image/kitchen134.webp',
'assets/image/kitchen135.webp',
'assets/image/kitchen136.webp',
'assets/image/kitchen137.webp',
'assets/image/kitchen138.webp',
'assets/image/kitchen139.webp',
'assets/image/kitchen140.webp',
'assets/image/kitchen141.webp',
'assets/image/kitchen142.webp',
'assets/image/kitchen143.webp',
'assets/image/kitchen144.webp',
'assets/image/kitchen145.webp',
'assets/image/kitchen146.webp',
'assets/image/kitchen147.webp',
'assets/image/kitchen148.webp',
'assets/image/kitchen149.webp',
'assets/image/kitchen150.webp',
'assets/image/kitchen151.webp',
'assets/image/kitchen152.webp',
'assets/image/kitchen153.webp',
'assets/image/kitchen154.webp',
'assets/image/kitchen155.webp',
'assets/image/kitchen156.webp',
'assets/image/kitchen157.webp',
'assets/image/kitchen158.webp',
'assets/image/kitchen159.webp',
'assets/image/kitchen160.webp',
'assets/image/kitchen161.webp',
'assets/image/kitchen162.webp',
'assets/image/kitchen163.webp',
'assets/image/kitchen164.webp',
'assets/image/kitchen165.webp',
'assets/image/kitchen166.webp',
'assets/image/kitchen167.webp',
'assets/image/kitchen168.webp',
'assets/image/kitchen169.webp',
'assets/image/kitchen170.webp',
'assets/image/kitchen171.webp',
'assets/image/kitchen172.webp',
'assets/image/kitchen173.webp',
'assets/image/kitchen174.webp',
'assets/image/kitchen175.webp',
'assets/image/kitchen176.webp',
'assets/image/kitchen177.webp',
'assets/image/kitchen178.webp',
'assets/image/kitchen179.webp',
'assets/image/kitchen180.webp',
'assets/image/kitchen181.webp',
'assets/image/kitchen182.webp',
'assets/image/kitchen183.webp',
'assets/image/kitchen184.webp',
'assets/image/kitchen185.webp',
'assets/image/kitchen186.webp',
'assets/image/kitchen187.webp',
'assets/image/kitchen188.webp',
'assets/image/kitchen189.webp',
'assets/image/kitchen190.webp',
'assets/image/kitchen191.webp',
'assets/image/kitchen192.webp',
'assets/image/kitchen193.webp',
'assets/image/kitchen194.webp',
'assets/image/kitchen195.webp',
'assets/image/kitchen196.webp',
'assets/image/kitchen197.webp',
'assets/image/kitchen198.webp',
'assets/image/kitchen199.webp',
'assets/image/kitchen200.webp',
];
//color
static const Color colorMainHeader = Color.fromRGBO(0,0,0,0.4);
static const Color colorMainBack = Color.fromRGBO(220, 220, 220, 1);
static const Color colorMainUiActiveColorFrom = Color.fromRGBO(255,30,80,1);
static const Color colorMainUiActiveColorTo = Color.fromRGBO(120,120,255,1);
static const Color colorSettingUiActiveColor = Colors.deepPurple;
static const Color colorUiInactiveColor = Color.fromRGBO(50,50,50,1);
//sound
static const String audioZero = 'assets/sound/zero.wav'; //無音1秒
static const List<String> audioHiyoko = [
'assets/sound/hiyoko1.wav',
'assets/sound/hiyoko2.wav',
'assets/sound/hiyoko3.wav',
'assets/sound/hiyoko4.wav',
'assets/sound/hiyoko5.wav',
'assets/sound/hiyoko6.wav',
];
}
lib/empty.dart
lib/language_support.dart
///
/// Consolidated language utilities.
///
library;
/// Usage:
/// await LanguageState.ensureInitialized();
/// final currentCode = await LanguageState.getLanguageCode();
/// await LanguageState.setLanguageCode('en');
/// final locale = parseLocaleTag(currentCode);
///
/// Use LanguageCatalog when presenting selectable language names.
/// Configure LanguageState.configure if you need custom storage.
///
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LanguageCatalog {
const LanguageCatalog._();
static const String prefLanguageCode = 'languageCode';
static const Map<String, String> names = {
'en': 'English',
'bg': 'Български',
'cs': 'Čeština',
'da': 'Dansk',
'de': 'Deutsch',
'el': 'Ελληνικά',
'es': 'Español',
'et': 'Eesti',
'fi': 'Suomi',
'fr': 'Français',
'hu': 'Magyar',
'it': 'Italiano',
'ja': '日本語',
'lt': 'Lietuvių',
'lv': 'Latviešu',
'nl': 'Nederlands',
'pl': 'Polski',
'pt': 'Português',
'ro': 'Română',
'ru': 'Русский',
'sk': 'Slovenčina',
'sv': 'Svenska',
'th': 'ไทย',
'zh': '中文',
};
static List<String> get supportedCodes => names.keys.toList(growable: false);
static List<Locale> buildSupportedLocales() {
return supportedCodes.map((code) => Locale(code)).toList(growable: false);
}
static String labelFor(String? code) {
if (code == null || code.isEmpty) {
return 'Default';
}
return names[code] ?? code;
}
}
abstract class LanguageStorage {
const LanguageStorage();
Future<void> saveLanguageCode(String code);
Future<String> loadLanguageCode();
}
class SharedPreferencesLanguageStorage extends LanguageStorage {
const SharedPreferencesLanguageStorage({
this.prefLanguageCode = LanguageCatalog.prefLanguageCode,
});
final String prefLanguageCode;
Future<SharedPreferences> _ensurePrefs() async {
return SharedPreferences.getInstance();
}
@override
Future<void> saveLanguageCode(String code) async {
final prefs = await _ensurePrefs();
await prefs.setString(prefLanguageCode, code);
}
@override
Future<String> loadLanguageCode() async {
final prefs = await _ensurePrefs();
return prefs.getString(prefLanguageCode) ?? '';
}
}
class LanguageState {
LanguageState._();
static LanguageStorage _storage = const SharedPreferencesLanguageStorage();
static String _languageCode = '';
static bool _initialized = false;
static Completer<void>? _initializing;
static String get currentCode => _languageCode;
static void configure({LanguageStorage? storage, String? initialCode}) {
if (storage != null) {
_storage = storage;
}
if (initialCode != null) {
_languageCode = initialCode;
_initialized = true;
}
}
static Future<void> ensureInitialized() async {
if (_initialized) {
return;
}
final completer = _initializing;
if (completer != null) {
await completer.future;
return;
}
final newCompleter = Completer<void>();
_initializing = newCompleter;
try {
_languageCode = await _storage.loadLanguageCode();
_initialized = true;
newCompleter.complete();
} catch (error, stackTrace) {
if (!newCompleter.isCompleted) {
newCompleter.completeError(error, stackTrace);
}
rethrow;
} finally {
_initializing = null;
}
}
static Future<void> setLanguageCode(String? code) async {
final value = code?.trim() ?? '';
_languageCode = value;
_initialized = true;
await _storage.saveLanguageCode(value);
}
static Future<String> getLanguageCode() async {
await ensureInitialized();
return _languageCode;
}
}
Locale? parseLocaleTag(String tag) {
if (tag.isEmpty) {
return null;
}
final parts = tag.split('-');
final language = parts.isNotEmpty ? parts[0] : tag;
String? script;
String? country;
if (parts.length >= 2) {
final p1 = parts[1];
if (p1.length == 4) {
script = p1;
} else {
country = p1;
}
}
if (parts.length >= 3) {
final p2 = parts[2];
if (p2.length == 4) {
script = p2;
} else {
country = p2;
}
}
return Locale.fromSubtags(
languageCode: language,
scriptCode: script,
countryCode: country,
);
}
lib/loading_screen.dart
import 'package:flutter/material.dart';
class LoadingScreen extends StatelessWidget {
const LoadingScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.indigo,
body: const Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.indigoAccent),
backgroundColor: Colors.white,
),
),
);
}
}
lib/main.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-02
///
library;
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'l10n/app_localizations.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart' if (dart.library.html) 'empty.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:wattconversion/language_support.dart';
import 'package:wattconversion/const_value.dart';
import 'package:wattconversion/version_state.dart';
import 'package:wattconversion/setting.dart';
import 'package:wattconversion/ad_manager.dart';
import 'package:wattconversion/ad_banner_widget.dart';
import 'package:wattconversion/preferences.dart';
import 'package:wattconversion/audio_play.dart';
import 'package:wattconversion/loading_screen.dart';
import 'package:wattconversion/theme_color.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb) {
MobileAds.instance.initialize();
}
await Preferences.initial();
await LanguageState.ensureInitialized();
runApp(const MainApp());
}
ThemeMode _themeModeFromNumber(int value) {
switch (value) {
case 1:
return ThemeMode.light;
case 2:
return ThemeMode.dark;
default:
return ThemeMode.system;
}
}
class MainApp extends StatefulWidget { //statefulに変更して言語変更に対応
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
Locale? localeLanguage;
ThemeMode themeMode = ThemeMode.system;
@override
void initState() {
super.initState();
localeLanguage = parseLocaleTag(LanguageState.currentCode);
themeMode = _themeModeFromNumber(Preferences.themeNumber);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: AppLocalizations.localizationsDelegates, //多言語化
supportedLocales: AppLocalizations.supportedLocales, //自動で言語リストを生成
locale: localeLanguage,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: ConstValue.colorMainBack),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: ConstValue.colorMainBack,
brightness: Brightness.dark,
),
useMaterial3: true,
),
themeMode: themeMode,
home: const MainHomePage(),
);
}
}
class MainHomePage extends StatefulWidget {
const MainHomePage({super.key});
@override
State<MainHomePage> createState() => _MainHomePageState();
}
class _MainHomePageState extends State<MainHomePage> with SingleTickerProviderStateMixin {
late AdManager _adManager;
late ThemeColor _themeColor;
final AudioPlay _audioPlay = AudioPlay(); //効果音
late AnimationController _animationController;
late Animation<double> _opacityAnimation;
bool _showBackImage = true;
int _backImageNumber = 0;
int _lastBackImageNumber = 0;
int _wattFrom = 600;
int _wattTo = 500;
int _minute = 5;
int _second = 0;
int _themeNumber = 0;
bool _isReady = false;
bool _isFirst = true;
//アプリのバージョン取得
void _getVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
setState(() {
VersionState.versionSave(packageInfo.version);
});
}
//言語準備
Future<void> _getCurrentLocale() async {
final String code = await LanguageState.getLanguageCode();
if (!mounted) {
return;
}
final mainState = context.findAncestorStateOfType<_MainAppState>();
if (mainState == null) {
return;
}
mainState
..localeLanguage = parseLocaleTag(code)
..setState(() {});
}
//Theme
void _getThemeColor() {
_themeColor = ThemeColor(themeNumber: _themeNumber, context: context);
}
void _applyThemeMode() {
final mainState = context.findAncestorStateOfType<_MainAppState>();
if (mainState == null) {
return;
}
mainState
..themeMode = _themeModeFromNumber(Preferences.themeNumber)
..setState(() {});
}
@override
void initState() {
super.initState();
_initState();
}
void _initState() async {
_getVersion();
_getCurrentLocale();
LanguageState.getLanguageCode();
_adManager = AdManager();
_audioPlay.playZero();
final int subSecond = (DateTime.now()).millisecondsSinceEpoch ~/ 100;
_backImageNumber = subSecond % ConstValue.imageBacks.length;
//background animation
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_opacityAnimation = Tween<double>(begin: 0, end: 1).animate(_animationController);
_animationController.addListener(() {
setState(() {});
});
_backImageChange();
//
await Preferences.initial();
_showBackImage = Preferences.showBackImage;
_audioPlay.soundVolume = Preferences.soundVolume;
_wattFrom = Preferences.wattFrom;
_wattTo = Preferences.wattTo;
_minute = Preferences.minute;
_second = Preferences.second;
_themeNumber = Preferences.themeNumber;
_applyThemeMode();
setState(() {
_isReady = true;
});
}
@override
void dispose() {
_adManager.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_isReady == false) {
return const LoadingScreen();
}
if (_isFirst) {
_isFirst = false;
_getThemeColor();
}
return Container(
decoration: const BoxDecoration(
color: ConstValue.colorMainBack,
),
child: Container(
decoration: _decoration2(),
child: Container(
decoration: _decoration1(),
child: Scaffold(
backgroundColor: Colors.transparent, //ここは透明
appBar: AppBar(
backgroundColor: ConstValue.colorMainHeader,
//タイトル表示
title: Text(AppLocalizations.of(context)!.title,
style: const TextStyle(
color: Colors.white,
fontSize: 12.0,
)
),
//設定ボタン
actions: <Widget>[
TextButton(
onPressed: () async {
bool? ret = await Navigator.of(context).push(
MaterialPageRoute<bool>(builder:(context) => const SettingPage()),
);
//awaitで呼び出しているので、settingから戻ったら以下が実行される。
if (ret!) { //設定で適用だった場合
await _getCurrentLocale();
_showBackImage = Preferences.showBackImage;
_audioPlay.soundVolume = Preferences.soundVolume;
_themeNumber = Preferences.themeNumber;
_getThemeColor();
_applyThemeMode();
setState(() {});
}
},
child: Text(
AppLocalizations.of(context)!.setting,
style: const TextStyle(
color: Colors.white,
fontSize: 12.0,
)
)
)
]
),
body: SafeArea(
child: GestureDetector(
onTap: () {
_audioPlay.play01();
_backImageChange();
},
child: Column(children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 5, left: 5, right: 5, bottom: 100),
child: Column(children: [
_content(),
])
)
)
),
])
)
),
bottomNavigationBar: AdBannerWidget(adManager: _adManager),
)
)
)
);
}
//背景画像前側
Decoration _decoration1() {
if (_showBackImage) {
return BoxDecoration(
image: DecorationImage(
image: AssetImage(ConstValue.imageBacks[_backImageNumber]),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black.withValues(alpha: _opacityAnimation.value),
BlendMode.dstATop,
),
)
);
} else {
return const BoxDecoration();
}
}
//背景画像後ろ側
Decoration _decoration2() {
if (_showBackImage) {
return BoxDecoration(
image: DecorationImage(
image: AssetImage(ConstValue.imageBacks[_lastBackImageNumber]),
fit: BoxFit.cover,
),
);
} else {
return const BoxDecoration();
}
}
void _backImageChange() {
final int subSecond = (DateTime.now()).millisecondsSinceEpoch ~/ 100;
_backImageNumber = subSecond % ConstValue.imageBacks.length;
_animationController.forward();
Future.delayed(const Duration(milliseconds: 600), () {
_lastBackImageNumber = _backImageNumber;
_animationController.reverse();
});
}
Widget _content() {
return Column(children:[
_widgetWattFrom(),
_widgetMinute(),
_widgetSecond(),
_widgetWattTo(),
_widgetResult(),
]);
}
Widget _widgetWattFrom() {
final l = AppLocalizations.of(context)!;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 4, right: 4, bottom: 0),
color: _themeColor.backColorMono.withValues(alpha: 0.9),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Padding(
padding: const EdgeInsets.only(top: 5, left: 10, right: 0, bottom: 0),
child: Row(children: [
Text(l.wattFrom),
const Spacer(),
])
),
Padding(
padding: const EdgeInsets.only(top: 0, left: 10, right: 0, bottom: 0),
child: Row(children: <Widget>[
Container(
color: _themeColor.backColorMono,
child: SizedBox(
width: 80,
child: Text(_wattFrom.toString(),textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge
),
)
),
Expanded(
child: Slider(
value: _wattFrom.toDouble(),
min: 300,
max: 1800,
divisions: 15,
onChanged: (double value) {
setState(() {
_wattFrom = value.toInt();
Preferences.setWattFrom(_wattFrom);
});
},
activeColor: ConstValue.colorMainUiActiveColorFrom,
inactiveColor: ConstValue.colorUiInactiveColor,
)
)
])
)
]
),
),
)
);
}
Widget _widgetMinute() {
final l = AppLocalizations.of(context)!;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 3, right: 4, bottom: 0),
color: _themeColor.backColorMono.withValues(alpha: 0.9),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Padding(
padding: const EdgeInsets.only(top: 5, left: 10, right: 0, bottom: 0),
child: Row(children: [
Text(l.fromMinute),
const Spacer(),
])
),
Padding(
padding: const EdgeInsets.only(top: 0, left: 10, right: 0, bottom: 0),
child: Row(children: <Widget>[
Container(
color: _themeColor.backColorMono,
child: SizedBox(
width: 80,
child: Text(_minute.toString(),textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge
),
)
),
Expanded(
child: Slider(
value: _minute.toDouble(),
min: 1,
max: 30,
divisions: 29,
onChanged: (double value) {
setState(() {
_minute = value.toInt();
Preferences.setMinute(_minute);
});
},
activeColor: ConstValue.colorMainUiActiveColorFrom,
inactiveColor: ConstValue.colorUiInactiveColor,
)
)
])
)
]
),
),
)
);
}
Widget _widgetSecond() {
final l = AppLocalizations.of(context)!;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 3, right: 4, bottom: 0),
color: _themeColor.backColorMono.withValues(alpha: 0.9),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(12),
bottomRight: Radius.circular(12),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Padding(
padding: const EdgeInsets.only(top: 5, left: 10, right: 0, bottom: 0),
child: Row(children: [
Text(l.fromSecond),
const Spacer(),
])
),
Padding(
padding: const EdgeInsets.only(top: 0, left: 10, right: 0, bottom: 0),
child: Row(children: <Widget>[
Container(
color: _themeColor.backColorMono,
child: SizedBox(
width: 80,
child: Text(_second.toString(),textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge
),
)
),
Expanded(
child: Slider(
value: _second.toDouble(),
min: 0,
max: 50,
divisions: 5,
onChanged: (double value) {
setState(() {
_second = value.toInt();
Preferences.setMinute(_second);
});
},
activeColor: ConstValue.colorMainUiActiveColorFrom,
inactiveColor: ConstValue.colorUiInactiveColor,
)
)
])
)
]
),
),
)
);
}
Widget _widgetWattTo() {
final l = AppLocalizations.of(context)!;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.backColorMono.withValues(alpha: 0.9),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Padding(
padding: const EdgeInsets.only(top: 5, left: 10, right: 0, bottom: 0),
child: Row(children: [
Text(l.wattTo),
const Spacer(),
])
),
Padding(
padding: const EdgeInsets.only(top: 0, left: 10, right: 0, bottom: 0),
child: Row(children: <Widget>[
Container(
color: _themeColor.backColorMono,
child: SizedBox(
width: 80,
child: Text(_wattTo.toString(),textAlign: TextAlign.center,
style: Theme.of(context).textTheme.titleLarge
),
)
),
Expanded(
child: Slider(
value: _wattTo.toDouble(),
min: 300,
max: 1800,
divisions: 15,
onChanged: (double value) {
setState(() {
_wattTo = value.toInt();
Preferences.setWattTo(_wattTo);
});
},
activeColor: ConstValue.colorMainUiActiveColorTo,
inactiveColor: ConstValue.colorUiInactiveColor,
)
)
])
),
]
),
),
)
);
}
Widget _widgetResult() {
final l = AppLocalizations.of(context)!;
final int sec = ((_minute * 60 + _second) / _wattTo * _wattFrom).toInt();
final int answerMinute = (sec / 60).floor();
final int answerSecond = sec % 60;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.backColorMono.withValues(alpha: 0.9),
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Row(children: [
const Spacer(),
Text('${l.specified} ${_wattFrom}W',
style: TextStyle(
color: ConstValue.colorMainUiActiveColorFrom,
fontSize: Theme.of(context).textTheme.titleLarge?.fontSize,
)
),
const Spacer(),
]),
Row(children: [
const Spacer(),
Text('${_minute} ${l.minute} ${_second} ${l.second}',
style: TextStyle(
color: ConstValue.colorMainUiActiveColorFrom,
fontSize: Theme.of(context).textTheme.titleLarge?.fontSize,
)
),
const Spacer(),
]),
SizedBox(height: 5),
Row(children: [
const Spacer(),
Text('${l.conversion} ${_wattTo}W',
style: TextStyle(
color: ConstValue.colorMainUiActiveColorTo,
fontSize: Theme.of(context).textTheme.titleLarge?.fontSize,
)
),
const Spacer(),
]),
Row(children: [
const Spacer(),
Text('${answerMinute} ${l.minute} ${answerSecond} ${l.second}',
style: TextStyle(
color: ConstValue.colorMainUiActiveColorTo,
fontSize: Theme.of(context).textTheme.titleLarge?.fontSize,
),
),
const Spacer(),
])
]
),
),
)
);
}
}
lib/preferences.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-26
///
library;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wattconversion/const_value.dart';
//デバイスに情報を保存
class Preferences {
static bool ready = false;
//この値は常に最新にしておく
static String _languageCode = '';
static bool _showBackImage = true;
static double _soundVolume = 0.3;
static int _wattFrom = 600;
static int _wattTo = 500;
static int _minute = 5;
static int _second = 0;
static int _themeNumber = 0;
static String get languageCode => _languageCode;
static bool get showBackImage => _showBackImage;
static double get soundVolume => _soundVolume;
static int get wattFrom => _wattFrom;
static int get wattTo => _wattTo;
static int get minute => _minute;
static int get second => _second;
static int get themeNumber => _themeNumber;
static Future<void> initial() async {
_languageCode = await getLanguageCode();
_showBackImage = await getShowBackImage();
_soundVolume = await getSoundVolume();
_wattFrom = await getWattFrom();
_wattTo = await getWattTo();
_minute = await getMinute();
_second = await getSecond();
_themeNumber = await getThemeNumber();
ready = true;
}
//----------------------------
//言語コード
static Future<void> setLanguageCode(String str) async {
_languageCode = str;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(ConstValue.prefLanguageCode, str);
}
static Future<String> getLanguageCode() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String str = prefs.getString(ConstValue.prefLanguageCode) ?? 'en';
return str;
}
//----------------------------
//背景画像表示
static Future<void> setShowBackImage(bool flag) async {
_showBackImage = flag;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool(ConstValue.prefShowBackImage, flag);
}
static Future<bool> getShowBackImage() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final bool flag = prefs.getBool(ConstValue.prefShowBackImage) ?? true;
return flag;
}
//----------------------------
//効果音音量
static Future<void> setSoundVolume(double num) async {
_soundVolume = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setDouble(ConstValue.prefSoundVolume, num);
}
static Future<double> getSoundVolume() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final double num = prefs.getDouble(ConstValue.prefSoundVolume) ?? 0.3;
return num;
}
//----------------------------
//watt from
static Future<void> setWattFrom(int num) async {
_wattFrom = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(ConstValue.prefWattFrom, num);
}
static Future<int> getWattFrom() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int num = prefs.getInt(ConstValue.prefWattFrom) ?? 600;
return num;
}
//----------------------------
//watt to
static Future<void> setWattTo(int num) async {
_wattTo = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(ConstValue.prefWattTo, num);
}
static Future<int> getWattTo() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int num = prefs.getInt(ConstValue.prefWattTo) ?? 500;
return num;
}
//----------------------------
//minute
static Future<void> setMinute(int num) async {
_minute = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(ConstValue.prefMinute, num);
}
static Future<int> getMinute() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int num = prefs.getInt(ConstValue.prefMinute) ?? 5;
return num;
}
//----------------------------
//second
static Future<void> setSecond(int num) async {
_second = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(ConstValue.prefSecond, num);
}
static Future<int> getSecond() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int num = prefs.getInt(ConstValue.prefSecond) ?? 0;
return num;
}
//----------------------------
//themeNumber
static Future<void> setThemeNumber(int num) async {
_themeNumber = num;
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt(ConstValue.prefThemeNumber, num);
}
static Future<int> getThemeNumber() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int num = prefs.getInt(ConstValue.prefThemeNumber) ?? 0;
return num;
}
//----------------------------
}
lib/setting.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-10-15
///
library;
import 'package:flutter/material.dart';
import 'l10n/app_localizations.dart';
import 'package:wattconversion/language_support.dart';
import 'package:wattconversion/preferences.dart';
import 'package:wattconversion/const_value.dart';
import 'package:wattconversion/version_state.dart';
import 'package:wattconversion/ad_manager.dart';
import 'package:wattconversion/ad_banner_widget.dart';
import 'package:wattconversion/loading_screen.dart';
import 'package:wattconversion/theme_color.dart';
class SettingPage extends StatefulWidget {
const SettingPage({super.key});
@override
State<SettingPage> createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
String? _languageKey;
late AdManager _adManager;
late ThemeColor _themeColor;
bool _showBackImage = true;
double _soundVolume = 0.0;
int _themeNumber = 0;
bool _isReady = false;
bool _isFirst = true;
@override
void initState() {
super.initState();
_initState();
}
void _initState() async {
_adManager = AdManager();
final String languageCode = await LanguageState.getLanguageCode();
if (!mounted) {
return;
}
_languageKey = languageCode.isEmpty ? null : languageCode;
await Preferences.initial();
if (!mounted) {
return;
}
_showBackImage = Preferences.showBackImage;
_soundVolume = Preferences.soundVolume;
_themeNumber = Preferences.themeNumber;
setState(() {
_isReady = true;
});
}
@override
void dispose() {
_adManager.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_isReady == false) {
return LoadingScreen();
}
if (_isFirst) {
_isFirst = false;
_themeColor = ThemeColor(themeNumber: _themeNumber, context: context);
}
final l = AppLocalizations.of(context)!;
return Scaffold(
backgroundColor: _themeColor.backColor,
appBar: AppBar(
centerTitle: true,
elevation: 0,
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
Navigator.of(context).pop(false);
},
),
title: Text(l.setting),
foregroundColor: _themeColor.appBarForegroundColor,
backgroundColor: Colors.transparent,
actions: [
IconButton(
icon: const Icon(Icons.check),
onPressed: () async {
await LanguageState.setLanguageCode(_languageKey);
await Preferences.setShowBackImage(_showBackImage);
await Preferences.setSoundVolume(_soundVolume);
await Preferences.setThemeNumber(_themeNumber);
if (!mounted) {
return;
}
Navigator.of(context).pop(true);
},
),
],
),
body: Column(children:[
Expanded(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), //背景タップでキーボードを仕舞う
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(left: 4, top: 4, right: 4, bottom: 100),
child: Column(children: [
_buildBackgroundImage(),
_buildVolume(),
_buildLanguage(),
_buildTheme(),
_buildUsage(),
_buildVersion(),
]),
),
),
),
),
]),
bottomNavigationBar: AdBannerWidget(adManager: _adManager),
);
}
Widget _buildBackgroundImage() {
final l = AppLocalizations.of(context)!;
return Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 16, left: 16, right: 16, bottom: 16),
child: Row(children:<Widget>[
Expanded(child: Text(l.showBackImage)),
Switch(
value: _showBackImage,
onChanged: (bool value) {
setState(() {
_showBackImage = value;
});
},
activeThumbColor: ConstValue.colorSettingUiActiveColor,
inactiveThumbColor: ConstValue.colorUiInactiveColor,
),
]),
),
],
),
);
}
Widget _buildVolume() {
final l = AppLocalizations.of(context)!;
return Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 18, left: 16, right: 16, bottom: 0),
child: Row(children: [
Text(l.soundVolume),
const Spacer(),
])
),
Padding(
padding: const EdgeInsets.only(top: 0, left: 16, right: 16, bottom: 6),
child: Row(children: <Widget>[
Text(_soundVolume.toString()),
Expanded(
child: Slider(
value: _soundVolume,
min: 0.0,
max: 1.0,
divisions: 10,
onChanged: (double value) {
setState(() {
_soundVolume = value;
});
},
activeColor: ConstValue.colorSettingUiActiveColor,
inactiveColor: ConstValue.colorUiInactiveColor,
)
)
])
),
],
),
);
}
Widget _buildLanguage() {
final l = AppLocalizations.of(context)!;
return Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
title: Text(l.language,style: Theme.of(context).textTheme.bodyMedium),
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
trailing: DropdownButton<String?>(
value: _languageKey,
dropdownColor: _themeColor.dropdownColor,
items: [
const DropdownMenuItem<String?>(
value: null,
child: Text('Default'),
),
...LanguageCatalog.names.entries.map(
(entry) => DropdownMenuItem<String?>(
value: entry.key,
child: Text(entry.value),
),
),
],
onChanged: (String? value) {
setState(() {
_languageKey = value;
});
},
),
),
],
),
),
);
}
Widget _buildTheme() {
final l = AppLocalizations.of(context)!;
return Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ListTile(
title: Text(l.theme,style: Theme.of(context).textTheme.bodyMedium),
contentPadding: const EdgeInsets.symmetric(horizontal: 0),
trailing: DropdownButton<int>(
value: _themeNumber,
dropdownColor: _themeColor.dropdownColor,
items: [
DropdownMenuItem(value: 0, child: Text(l.systemDefault)),
DropdownMenuItem(value: 1, child: Text(l.lightTheme)),
DropdownMenuItem(value: 2, child: Text(l.darkTheme)),
],
onChanged: (int? value) {
if (value == null) {
return;
}
setState(() {
_themeNumber = value;
});
},
),
),
],
),
),
);
}
Widget _buildUsage() {
final l = AppLocalizations.of(context)!;
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:[
Text(l.usage1),
const SizedBox(height:15),
Text(l.usage2),
if (l.usage3 != '') Column(children: [
const SizedBox(height:15),
Text(l.usage3),
]),
if (l.usage4 != '') Column(children: [
const SizedBox(height:15),
Text(l.usage4),
])
]
),
),
)
);
}
Widget _buildVersion() {
return SizedBox(
width: double.infinity,
child: Card(
margin: const EdgeInsets.only(left: 4, top: 12, right: 4, bottom: 0),
color: _themeColor.cardColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Center(
child: Text(
'version ${VersionState.versionLoad()}',
style: const TextStyle(fontSize: 10),
),
),
),
),
);
}
}
lib/version_state.dart
///
/// @author akira ohmachi
/// @copyright ao-system, Inc.
/// @date 2023-01-27
///
library;
class VersionState {
static String _version = '';
//バージョンを記録
static void versionSave(String str) {
_version = str;
}
//バージョンを返す
static String versionLoad() {
return _version;
}
}