diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..7a8a412 --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +import 'package:mini_tias/screens/home_screen.dart'; + +/// MiniTIAS アプリのルートウィジェット. +class MiniTiasApp extends StatelessWidget { + const MiniTiasApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'MiniTIAS', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueGrey), + useMaterial3: true, + ), + home: const HomeScreen(), + debugShowCheckedModeBanner: false, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 244a702..87089bf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,122 +1,22 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; + +import 'package:mini_tias/app.dart'; +import 'package:mini_tias/providers/camera_provider.dart'; +import 'package:mini_tias/providers/gallery_provider.dart'; void main() { - runApp(const MyApp()); -} + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: .fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: .center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => CameraProvider()), + ChangeNotifierProvider(create: (_) => GalleryProvider()), + ], + child: const MiniTiasApp(), + ), + ); } diff --git a/lib/providers/camera_provider.dart b/lib/providers/camera_provider.dart new file mode 100644 index 0000000..5f7db77 --- /dev/null +++ b/lib/providers/camera_provider.dart @@ -0,0 +1,6 @@ +import 'package:flutter/foundation.dart'; + +/// カメラの初期化・プレビュー制御・撮影実行・ライフサイクルを管理する. +/// +/// Step 1 では空の Provider.Step 2 以降でカメラ機能を実装する. +class CameraProvider extends ChangeNotifier {} diff --git a/lib/providers/gallery_provider.dart b/lib/providers/gallery_provider.dart new file mode 100644 index 0000000..510e711 --- /dev/null +++ b/lib/providers/gallery_provider.dart @@ -0,0 +1,6 @@ +import 'package:flutter/foundation.dart'; + +/// 画像一覧の取得・キャッシュ・削除操作を管理する. +/// +/// Step 1 では空の Provider.Step 4 以降で一覧機能を実装する. +class GalleryProvider extends ChangeNotifier {} diff --git a/lib/screens/capture_screen.dart b/lib/screens/capture_screen.dart new file mode 100644 index 0000000..93afb58 --- /dev/null +++ b/lib/screens/capture_screen.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +/// 撮影画面.カメラプレビューとシャッターボタンを表示する. +/// +/// Step 1 では空のスキャフォールドのみ.Step 2 以降でカメラ機能を実装する. +class CaptureScreen extends StatelessWidget { + const CaptureScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: Text('撮影画面')); + } +} diff --git a/lib/screens/gallery_screen.dart b/lib/screens/gallery_screen.dart new file mode 100644 index 0000000..9136dc9 --- /dev/null +++ b/lib/screens/gallery_screen.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +/// 一覧画面.撮影済み画像のサムネイルグリッドを表示する. +/// +/// Step 1 では空のスキャフォールドのみ.Step 4 以降で一覧機能を実装する. +class GalleryScreen extends StatelessWidget { + const GalleryScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: Text('一覧画面')); + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart new file mode 100644 index 0000000..c7d73c8 --- /dev/null +++ b/lib/screens/home_screen.dart @@ -0,0 +1,96 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:screen_brightness/screen_brightness.dart'; + +import 'package:mini_tias/screens/capture_screen.dart'; +import 'package:mini_tias/screens/gallery_screen.dart'; + +/// BottomNavigationBar で撮影画面と一覧画面を切り替えるホーム画面. +/// +/// UI 全体を 180° 回転して表示する(端末を逆さに置いて使用するため). +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + int _currentIndex = 0; + double _brightness = 0.8; + + static const _screens = [CaptureScreen(), GalleryScreen()]; + + @override + void initState() { + super.initState(); + _setBrightness(_brightness); + } + + @override + void dispose() { + ScreenBrightness.instance.resetApplicationScreenBrightness(); + super.dispose(); + } + + Future _setBrightness(double value) async { + await ScreenBrightness.instance.setApplicationScreenBrightness(value); + } + + @override + Widget build(BuildContext context) { + final screenHeight = MediaQuery.of(context).size.height; + final attachmentPadding = screenHeight / 3; + + return Transform.rotate( + angle: math.pi, + child: Scaffold( + body: Padding( + padding: EdgeInsets.only(bottom: attachmentPadding), + child: _screens[_currentIndex], + ), + bottomNavigationBar: Padding( + padding: EdgeInsets.only(bottom: attachmentPadding), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + const Icon(Icons.brightness_low, size: 20), + Expanded( + child: Slider( + value: _brightness, + onChanged: (value) { + setState(() => _brightness = value); + _setBrightness(value); + }, + ), + ), + const Icon(Icons.brightness_high, size: 20), + ], + ), + ), + BottomNavigationBar( + currentIndex: _currentIndex, + onTap: (index) => setState(() => _currentIndex = index), + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.camera_alt), + label: '撮影', + ), + BottomNavigationBarItem( + icon: Icon(Icons.photo_library), + label: '一覧', + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..e93ef1f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import screen_brightness_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index ec64d1f..2820fd7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -131,6 +131,14 @@ url: "https://pub.dev" source: hosted version: "1.17.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -139,6 +147,78 @@ url: "https://pub.dev" source: hosted version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" + url: "https://pub.dev" + source: hosted + version: "6.1.5+1" + screen_brightness: + dependency: "direct main" + description: + name: screen_brightness + sha256: "5f70754028f169f059fdc61112a19dcbee152f8b293c42c848317854d650cba3" + url: "https://pub.dev" + source: hosted + version: "2.1.7" + screen_brightness_android: + dependency: transitive + description: + name: screen_brightness_android + sha256: d34f5321abd03bc3474f4c381f53d189117eba0b039eac1916aa92cca5fd0a96 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + screen_brightness_ios: + dependency: transitive + description: + name: screen_brightness_ios + sha256: "2493953340ecfe8f4f13f61db50ce72533a55b0bbd58ba1402893feecf3727f5" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + screen_brightness_macos: + dependency: transitive + description: + name: screen_brightness_macos + sha256: "4edf330ad21078686d8bfaf89413325fbaf571dcebe1e89254d675a3f288b5b9" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + screen_brightness_ohos: + dependency: transitive + description: + name: screen_brightness_ohos + sha256: a93a263dcd39b5c56e589eb495bcd001ce65cdd96ff12ab1350683559d5c5bb7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + screen_brightness_platform_interface: + dependency: transitive + description: + name: screen_brightness_platform_interface + sha256: "737bd47b57746bc4291cab1b8a5843ee881af499514881b0247ec77447ee769c" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + screen_brightness_windows: + dependency: transitive + description: + name: screen_brightness_windows + sha256: d3518bf0f5d7a884cee2c14449ae0b36803802866de09f7ef74077874b6b2448 + url: "https://pub.dev" + source: hosted + version: "2.1.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index da41d0c..3907161 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,8 @@ # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + provider: ^6.1.5+1 + screen_brightness: ^2.1.7 dev_dependencies: flutter_test: diff --git a/test/widget_test.dart b/test/widget_test.dart index 6b796bd..740eee2 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,30 +1,41 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; -import 'package:mini_tias/main.dart'; +import 'package:mini_tias/app.dart'; +import 'package:mini_tias/providers/camera_provider.dart'; +import 'package:mini_tias/providers/gallery_provider.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + testWidgets('アプリ起動時に撮影画面が表示される', (WidgetTester tester) async { + await tester.pumpWidget( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => CameraProvider()), + ChangeNotifierProvider(create: (_) => GalleryProvider()), + ], + child: const MiniTiasApp(), + ), + ); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + expect(find.text('撮影画面'), findsOneWidget); + expect(find.text('撮影'), findsOneWidget); + expect(find.text('一覧'), findsOneWidget); + }); - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + testWidgets('一覧タブをタップすると一覧画面に切り替わる', (WidgetTester tester) async { + await tester.pumpWidget( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => CameraProvider()), + ChangeNotifierProvider(create: (_) => GalleryProvider()), + ], + child: const MiniTiasApp(), + ), + ); - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + await tester.tap(find.text('一覧')); + await tester.pumpAndSettle(); + + expect(find.text('一覧画面'), findsOneWidget); }); } diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..81f96b7 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + ScreenBrightnessWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..94c966e 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + screen_brightness_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST