...
This commit is contained in:
@@ -12,6 +12,7 @@ android {
|
||||
|
||||
// [수정할 부분]
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
// 기존 1.8 -> 17로 변경
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
@@ -46,6 +47,12 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// [추가] 디슈가링 라이브러리 추가 (필수)
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||
}
|
||||
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
@@ -15,6 +15,17 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.speech.RecognitionService" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:label="playwith_app"
|
||||
android:name="${applicationName}"
|
||||
|
||||
@@ -5,6 +5,9 @@ PODS:
|
||||
- bonsoir_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- CwlCatchException (2.2.1):
|
||||
- CwlCatchExceptionSupport (~> 2.2.1)
|
||||
- CwlCatchExceptionSupport (2.2.1)
|
||||
- DKImagePickerController/Core (4.3.9):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
@@ -40,6 +43,11 @@ PODS:
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_local_notifications (0.0.1):
|
||||
- Flutter
|
||||
- gal (1.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- GoogleDataTransport (9.4.1):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
@@ -102,6 +110,13 @@ PODS:
|
||||
- SDWebImage (5.21.1):
|
||||
- SDWebImage/Core (= 5.21.1)
|
||||
- SDWebImage/Core (5.21.1)
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- speech_to_text (7.2.0):
|
||||
- CwlCatchException
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqlite3 (3.50.4):
|
||||
- sqlite3/common (= 3.50.4)
|
||||
- sqlite3/common (3.50.4)
|
||||
@@ -134,14 +149,20 @@ DEPENDENCIES:
|
||||
- bonsoir_darwin (from `.symlinks/plugins/bonsoir_darwin/darwin`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
|
||||
- gal (from `.symlinks/plugins/gal/darwin`)
|
||||
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
|
||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
|
||||
- objective_c (from `.symlinks/plugins/objective_c/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- speech_to_text (from `.symlinks/plugins/speech_to_text/darwin`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- CwlCatchException
|
||||
- CwlCatchExceptionSupport
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- GoogleDataTransport
|
||||
@@ -169,6 +190,10 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_local_notifications:
|
||||
:path: ".symlinks/plugins/flutter_local_notifications/ios"
|
||||
gal:
|
||||
:path: ".symlinks/plugins/gal/darwin"
|
||||
image_picker_ios:
|
||||
:path: ".symlinks/plugins/image_picker_ios/ios"
|
||||
mobile_scanner:
|
||||
@@ -177,16 +202,24 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/objective_c/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
shared_preferences_foundation:
|
||||
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||
speech_to_text:
|
||||
:path: ".symlinks/plugins/speech_to_text/darwin"
|
||||
sqlite3_flutter_libs:
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
audioplayers_darwin: 4027b33a8f471d996c13f71cb77f0b1583b5d923
|
||||
bonsoir_darwin: e3b8526c42ca46a885142df84229131dfabea842
|
||||
CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a
|
||||
CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||
file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49
|
||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
gal: 6a522c75909f1244732d4596d11d6a2f86ff37a5
|
||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||
GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065
|
||||
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
||||
@@ -204,6 +237,8 @@ SPEC CHECKSUMS:
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
SDWebImage: f29024626962457f3470184232766516dee8dfea
|
||||
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
|
||||
speech_to_text: 87bf9298952e8d9073be1b6aade6d5758db5170c
|
||||
sqlite3: 73513155ec6979715d3904ef53a8d68892d4032b
|
||||
sqlite3_flutter_libs: 86f82662868ee26ff3451f73cac9c5fc2a1f57fa
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
|
||||
@@ -56,12 +56,16 @@
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>QR 코드를 스캔하여 방에 접속하기 위해 카메라 권한이 필요합니다.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>채팅방에 사진을 공유하기 위해 갤러리 접근 권한이 필요합니다.</string>
|
||||
|
||||
<string>채팅방에 사진을 공유하기 위해 갤러리 접근 권한이 필요합니다.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>사진을 찍어 공유하기 위해 카메라 권한이 필요합니다.</string>
|
||||
|
||||
<string>사진을 찍어 공유하기 위해 카메라 권한이 필요합니다.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>동영상 촬영을 위해 마이크 권한이 필요합니다.</string>
|
||||
<string>동영상 촬영을 위해 마이크 권한이 필요합니다.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>이미지를 갤러리에 저장하기 위해 권한이 필요합니다.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>정답을 음성으로 말하기 위해 마이크 권한이 필요합니다.</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>말한 내용을 텍스트로 변환하여 정답을 확인합니다.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:playwith_core/playwith_core.dart'; // SettingsNotifier
|
||||
import 'intro_view.dart';
|
||||
|
||||
class IntroScreen extends StatelessWidget {
|
||||
final WidgetBuilder nextScreenBuilder;
|
||||
|
||||
const IntroScreen({
|
||||
super.key,
|
||||
required this.nextScreenBuilder,
|
||||
});
|
||||
|
||||
void _navigateToNextScreen(BuildContext context) {
|
||||
// 인트로 종료 후 다음 화면(Lobby)으로 이동 (뒤로가기 불가)
|
||||
Navigator.of(context).pushReplacement(
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation, secondaryAnimation) => nextScreenBuilder(context),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
return FadeTransition(opacity: animation, child: child);
|
||||
},
|
||||
transitionDuration: const Duration(milliseconds: 800), // 부드러운 전환
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// SettingsNotifier 싱글톤에서 현재 색상 가져오기
|
||||
final Color currentColor = SettingsNotifier().currentColor;
|
||||
|
||||
return Scaffold(
|
||||
// 배경색은 테마 배경색 사용
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
body: Center(
|
||||
child: IntroViewFlutter(
|
||||
mainColor: currentColor,
|
||||
onAnimationFinished: () {
|
||||
_navigateToNextScreen(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// "SBSPACE"를 한 줄로 그리는 IntroView
|
||||
class IntroViewFlutter extends StatefulWidget {
|
||||
final Color mainColor;
|
||||
final VoidCallback onAnimationFinished;
|
||||
|
||||
const IntroViewFlutter({
|
||||
super.key,
|
||||
required this.mainColor,
|
||||
required this.onAnimationFinished,
|
||||
});
|
||||
|
||||
@override
|
||||
State<IntroViewFlutter> createState() => _IntroViewFlutterState();
|
||||
}
|
||||
|
||||
class _IntroViewFlutterState extends State<IntroViewFlutter>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<int> _logoTextAnimation;
|
||||
late final Animation<int> _missionTextAnimation;
|
||||
|
||||
static const String _logoString = "SBSPACE";
|
||||
static const String _missionString = "Simple is Best.";
|
||||
// [참고] 폰트가 없으면 기본 폰트로 나옵니다. 에셋에 폰트 추가가 필요할 수 있습니다.
|
||||
static const String _fontFamily = "Sdmisaeng";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
const int logoDuration = _logoString.length * 150;
|
||||
const int missionDuration = _missionString.length * 100;
|
||||
final int totalAnimationDuration = logoDuration + missionDuration;
|
||||
|
||||
_controller = AnimationController(
|
||||
duration: Duration(milliseconds: totalAnimationDuration),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_logoTextAnimation = IntTween(begin: 0, end: _logoString.length).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(0.0, logoDuration / totalAnimationDuration, curve: Curves.linear),
|
||||
),
|
||||
);
|
||||
|
||||
_missionTextAnimation = IntTween(begin: 0, end: _missionString.length).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Interval(logoDuration / totalAnimationDuration, 1.0, curve: Curves.linear),
|
||||
),
|
||||
);
|
||||
|
||||
_controller.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (mounted) widget.onAnimationFinished();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_controller.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return CustomPaint(
|
||||
painter: _IntroPainter(
|
||||
mainColor: widget.mainColor,
|
||||
fontFamily: _fontFamily,
|
||||
logoTextLength: _logoTextAnimation.value,
|
||||
missionTextLength: _missionTextAnimation.value,
|
||||
),
|
||||
size: Size.infinite,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IntroPainter extends CustomPainter {
|
||||
final Color mainColor;
|
||||
final String fontFamily;
|
||||
final int logoTextLength;
|
||||
final int missionTextLength;
|
||||
|
||||
static const String _logoString = "SBSPACE";
|
||||
static const String _missionString = "Simple is Best.";
|
||||
|
||||
_IntroPainter({
|
||||
required this.mainColor,
|
||||
required this.fontFamily,
|
||||
required this.logoTextLength,
|
||||
required this.missionTextLength,
|
||||
});
|
||||
|
||||
TextSpan _buildLogoSpan(int length) {
|
||||
final Color normalColor = mainColor.withOpacity(0.6);
|
||||
final List<TextSpan> children = [];
|
||||
|
||||
if (length >= 1) children.add(TextSpan(text: "S", style: TextStyle(color: mainColor)));
|
||||
if (length >= 2) children.add(TextSpan(text: "B", style: TextStyle(color: mainColor)));
|
||||
if (length >= 3) {
|
||||
final String spaceToDraw = _logoString.substring(2, length.clamp(2, _logoString.length));
|
||||
children.add(TextSpan(text: spaceToDraw, style: TextStyle(color: normalColor)));
|
||||
}
|
||||
|
||||
return TextSpan(
|
||||
style: TextStyle(fontFamily: fontFamily, fontWeight: FontWeight.bold),
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
TextPainter _createTextPainter(TextSpan textSpan, double fontSize) {
|
||||
final style = textSpan.style!.copyWith(fontSize: fontSize);
|
||||
final painter = TextPainter(
|
||||
text: TextSpan(children: textSpan.children, style: style),
|
||||
textDirection: TextDirection.ltr,
|
||||
);
|
||||
painter.layout();
|
||||
return painter;
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final double logoFontSize = size.shortestSide / 6.0;
|
||||
|
||||
const double tempMissionFontSize = 100.0;
|
||||
final TextPainter tpMissionTemp = TextPainter(
|
||||
text: TextSpan(
|
||||
text: _missionString,
|
||||
style: TextStyle(fontFamily: fontFamily, fontWeight: FontWeight.bold, fontSize: tempMissionFontSize)
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
final double targetWidth = size.width * 0.9;
|
||||
final double scale = targetWidth / tpMissionTemp.width;
|
||||
final double missionFontSize = tempMissionFontSize * scale;
|
||||
|
||||
final tpLogoFull = _createTextPainter(_buildLogoSpan(_logoString.length), logoFontSize);
|
||||
|
||||
final TextPainter tpMissionFull = TextPainter(
|
||||
text: TextSpan(
|
||||
text: _missionString,
|
||||
style: TextStyle(
|
||||
color: mainColor,
|
||||
fontSize: missionFontSize,
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: FontWeight.bold
|
||||
)
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
final double padding = logoFontSize * 0.1;
|
||||
final double totalHeight = tpLogoFull.height + padding + tpMissionFull.height;
|
||||
final double startyLogo = (size.height - totalHeight) / 2.0;
|
||||
final double startyMission = startyLogo + tpLogoFull.height + padding;
|
||||
|
||||
final double startxLogo = (size.width - tpLogoFull.width) / 2.0;
|
||||
final double startxMission = (size.width - tpMissionFull.width) / 2.0;
|
||||
|
||||
final tpLogoSub = _createTextPainter(_buildLogoSpan(logoTextLength), logoFontSize);
|
||||
tpLogoSub.paint(canvas, Offset(startxLogo, startyLogo));
|
||||
|
||||
final String missionToDraw = _missionString.substring(0, missionTextLength);
|
||||
final tpMissionSub = TextPainter(
|
||||
text: TextSpan(text: missionToDraw, style: tpMissionFull.text!.style),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
|
||||
tpMissionSub.paint(canvas, Offset(startxMission, startyMission));
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _IntroPainter oldDelegate) {
|
||||
return oldDelegate.mainColor != mainColor ||
|
||||
oldDelegate.logoTextLength != logoTextLength ||
|
||||
oldDelegate.missionTextLength != missionTextLength;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import 'dart:io'; // Platform 확인용
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:playwith_core/playwith_core.dart';
|
||||
import 'lobby_screen.dart';
|
||||
|
||||
class IntroScreen extends StatefulWidget {
|
||||
const IntroScreen({super.key});
|
||||
|
||||
@override
|
||||
State<IntroScreen> createState() => _IntroScreenState();
|
||||
}
|
||||
|
||||
class _IntroScreenState extends State<IntroScreen> {
|
||||
final _nicknameController = TextEditingController();
|
||||
|
||||
Future<void> _enterLobby() async {
|
||||
if (_nicknameController.text.trim().isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("닉네임을 입력해주세요.")),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// [수정됨] 플랫폼별 권한 분기 처리
|
||||
if (Platform.isAndroid) {
|
||||
// 🤖 안드로이드: 명시적 권한 요청 필요
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
Permission.location, // 안드로이드 12 이하
|
||||
Permission.nearbyWifiDevices, // 안드로이드 13 이상
|
||||
].request();
|
||||
|
||||
// 로그 확인용
|
||||
bool isNearby = statuses[Permission.nearbyWifiDevices]?.isGranted ?? false;
|
||||
bool isLocation = statuses[Permission.location]?.isGranted ?? false;
|
||||
print("Android 권한 Check: Nearby=$isNearby, Location=$isLocation");
|
||||
|
||||
// 둘 다 거부되면 진행 불가 (단, 버전에 따라 하나만 있어도 됨)
|
||||
// 여기서는 "둘 다 false일 때만" 막는 것으로 완화
|
||||
if (!isNearby && !isLocation) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("❌ 안드로이드는 권한이 필요합니다.")),
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else if (Platform.isIOS) {
|
||||
// 🍎 iOS: 별도 요청 불필요
|
||||
// Info.plist에 설정만 잘 되어 있다면,
|
||||
// NetworkManager가 start() 될 때 시스템이 알아서 물어봅니다.
|
||||
print("iOS는 권한 체크를 건너뜁니다. (실행 시 자동 팝업됨)");
|
||||
}
|
||||
|
||||
// 초기화 및 입장
|
||||
NetworkManager().initialize(nickname: _nicknameController.text.trim());
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const LobbyScreen()),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('PlayWith', style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 40),
|
||||
TextField(
|
||||
controller: _nicknameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '닉네임을 입력하세요',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: _enterLobby,
|
||||
style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 50)),
|
||||
child: const Text('입장하기'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:playwith_core/playwith_core.dart';
|
||||
import 'lobby_screen.dart';
|
||||
import 'screens/settings_screen.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> {
|
||||
final _nicknameController = TextEditingController();
|
||||
final _settings = SettingsNotifier();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// 저장된 닉네임 반영
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (mounted && _settings.nickname.isNotEmpty) {
|
||||
setState(() {
|
||||
_nicknameController.text = _settings.nickname;
|
||||
});
|
||||
}
|
||||
});
|
||||
_settings.addListener(_syncSettings);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_settings.removeListener(_syncSettings);
|
||||
_nicknameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _syncSettings() {
|
||||
if (_nicknameController.text != _settings.nickname) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_nicknameController.text = _settings.nickname;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _enterLobby() async {
|
||||
final inputNick = _nicknameController.text.trim();
|
||||
if (inputNick.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("닉네임을 입력해주세요.")));
|
||||
return;
|
||||
}
|
||||
|
||||
// 안드로이드 권한 체크
|
||||
if (Platform.isAndroid) {
|
||||
Map<Permission, PermissionStatus> statuses = await [
|
||||
Permission.location,
|
||||
Permission.nearbyWifiDevices,
|
||||
].request();
|
||||
|
||||
bool isNearby = statuses[Permission.nearbyWifiDevices]?.isGranted ?? false;
|
||||
bool isLocation = statuses[Permission.location]?.isGranted ?? false;
|
||||
if (!isNearby && !isLocation) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("⚠️ 권한 허용이 필요합니다.")));
|
||||
}
|
||||
}
|
||||
|
||||
// 변경 사항 저장
|
||||
if (inputNick != _settings.nickname) {
|
||||
await _settings.setProfile(inputNick, _settings.avatarIndex);
|
||||
}
|
||||
|
||||
// [수정] 초기화 시 닉네임과 이미지를 함께 전달
|
||||
NetworkManager().initialize(
|
||||
nickname: _settings.nickname,
|
||||
profileImage: _settings.profileImageBase64, // [추가]
|
||||
);
|
||||
|
||||
if (!mounted) return;
|
||||
Navigator.push(context, MaterialPageRoute(builder: (_) => const LobbyScreen()));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings, color: Colors.grey),
|
||||
tooltip: "설정",
|
||||
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreen())),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(32.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('PlayWith', style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// [핵심] AvatarWidget 사용 (Core 컴포넌트)
|
||||
ListenableBuilder(
|
||||
listenable: _settings,
|
||||
builder: (context, _) {
|
||||
return GestureDetector(
|
||||
onTap: () => _settings.pickProfileImage(),
|
||||
child: Stack(
|
||||
children: [
|
||||
AvatarWidget(
|
||||
base64Image: _settings.profileImageBase64,
|
||||
colorValue: Colors.primaries[_settings.avatarIndex % Colors.primaries.length].value,
|
||||
nickname: _nicknameController.text,
|
||||
size: 120,
|
||||
),
|
||||
Positioned(
|
||||
right: 0, bottom: 0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: const BoxDecoration(color: Colors.blue, shape: BoxShape.circle),
|
||||
child: const Icon(Icons.camera_alt, size: 20, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
TextField(
|
||||
controller: _nicknameController,
|
||||
textAlign: TextAlign.center,
|
||||
decoration: const InputDecoration(
|
||||
labelText: '닉네임',
|
||||
border: OutlineInputBorder(),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
ElevatedButton(
|
||||
onPressed: _enterLobby,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size(double.infinity, 50),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
child: const Text('입장하기', style: TextStyle(fontSize: 18)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+44
-32
@@ -1,21 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:playwith_core/playwith_core.dart';
|
||||
import 'package:playwith_game_quiz/quiz_game.dart'; // 퀴즈 모듈 import
|
||||
import 'intro_screen.dart';
|
||||
import 'package:playwith_game_quiz/quiz_game.dart';
|
||||
import 'login_screen.dart'; // [수정] 인트로 스크린 import (경로가 다르면 수정 필요)
|
||||
import 'intro/intro_screen.dart'; // 만약 intro 폴더에 넣으셨다면 이 경로 사용
|
||||
import 'lobby_screen.dart';
|
||||
|
||||
void main() {
|
||||
// 1. 플러터 바인딩 초기화
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 2. 사운드 리소스 주입 (여기가 핵심!)
|
||||
// AssetSource는 'assets/' 접두어를 자동으로 붙이므로 그 하위 경로만 적습니다.
|
||||
|
||||
SoundManager().initialize(soundPaths: {
|
||||
SoundKey.bgm: 'audio/bgm.mp3',
|
||||
SoundKey.correct: 'audio/correct.mp3',
|
||||
SoundKey.wrong: 'audio/wrong.mp3',
|
||||
SoundKey.win: 'audio/win.mp3',
|
||||
SoundKey.click: 'audio/correct.mp3',
|
||||
});
|
||||
|
||||
await NotificationManager().initialize();
|
||||
|
||||
runApp(const PlayWithApp());
|
||||
}
|
||||
|
||||
@@ -28,43 +30,53 @@ class PlayWithApp extends StatefulWidget {
|
||||
|
||||
class _PlayWithAppState extends State<PlayWithApp> {
|
||||
final _net = NetworkManager();
|
||||
final _settings = SettingsNotifier();
|
||||
|
||||
// 등록된 게임 목록
|
||||
final List<BaseGame> _games = [
|
||||
QuizGame(), // 여기서 등록!
|
||||
QuizGame(),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// [전역 라우팅] 네트워크 메시지를 감시하다가 'GAME_START'가 오면 해당 게임 실행
|
||||
_net.messageStream.listen((data) {
|
||||
if (data['type'] == 'GAME_START') {
|
||||
final String gameId = data['gameId'];
|
||||
|
||||
// ID에 맞는 게임 찾기
|
||||
final game = _games.firstWhere(
|
||||
(g) => g.id == gameId,
|
||||
orElse: () => throw Exception("Game not found: $gameId")
|
||||
);
|
||||
|
||||
// 게임 화면으로 이동 (네비게이터 키를 안 쓰고 있어서 간단히 처리 불가, 아래 설명 참조)
|
||||
// 실제로는 GlobalKey<NavigatorState>를 쓰거나, 현재 context를 찾아야 함.
|
||||
// MVP에서는 LobbyScreen 내부에서 처리하는 것이 안전함.
|
||||
}
|
||||
// 라우팅 로직...
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'PlayWith',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const IntroScreen(),
|
||||
return ListenableBuilder(
|
||||
listenable: _settings,
|
||||
builder: (context, child) {
|
||||
return MaterialApp(
|
||||
title: 'PlayWith',
|
||||
theme: _settings.currentTheme,
|
||||
themeMode: _settings.currentThemeMode,
|
||||
builder: (context, child) {
|
||||
return MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(
|
||||
textScaler: TextScaler.linear(_settings.fontScale),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
// [핵심 수정] 앱 시작 시 IntroScreen을 먼저 보여줌
|
||||
// nextScreenBuilder를 통해 애니메이션 종료 후 갈 곳(Lobby) 지정
|
||||
home: IntroScreen(
|
||||
nextScreenBuilder: (context) => const LoginScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [팁] 인트로와 닉네임 입력(IntroScreen.dart의 기존 로직)을 연결하기 위한 래퍼
|
||||
// 기존에 있던 닉네임 입력 화면(IntroScreen)과 이름이 겹치므로,
|
||||
// 기존의 닉네임 입력 화면은 'LoginScreen'이나 'NameInputScreen'으로 이름을 바꾸는 게 좋습니다.
|
||||
// 만약 'IntroScreen' 파일이 닉네임 입력 화면이었다면,
|
||||
// 이번에 만든 애니메이션 화면을 'SplashAnimationScreen' 등으로 이름을 지어서 구분해주세요.
|
||||
|
||||
// 여기서는 이번에 만든 애니메이션 화면을 'IntroAnimationScreen'이라고 가정하고,
|
||||
// 애니메이션이 끝나면 -> 닉네임 입력 화면(기존 IntroScreen) -> 로비 순서로 가는 게 자연스럽습니다.
|
||||
@@ -0,0 +1,187 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:playwith_core/playwith_core.dart'; // AvatarWidget 포함됨
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SettingsScreen> createState() => _SettingsScreenState();
|
||||
}
|
||||
|
||||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
final _nickController = TextEditingController();
|
||||
final _settings = SettingsNotifier();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nickController.text = _settings.nickname;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nickController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("설정")),
|
||||
body: ListenableBuilder(
|
||||
listenable: _settings,
|
||||
builder: (context, _) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// 1. 프로필 설정 섹션
|
||||
_buildSectionTitle("프로필 설정"),
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
// 아바타 변경 영역
|
||||
GestureDetector(
|
||||
onTap: () => _settings.pickProfileImage(),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
AvatarWidget(
|
||||
base64Image: _settings.profileImageBase64,
|
||||
colorValue: Colors.primaries[_settings.avatarIndex % Colors.primaries.length].value,
|
||||
nickname: _nickController.text,
|
||||
size: 100,
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: const BoxDecoration(color: Colors.blue, shape: BoxShape.circle),
|
||||
child: const Icon(Icons.edit, size: 16, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (_settings.profileImageBase64 != null)
|
||||
TextButton(
|
||||
onPressed: () => _settings.clearProfileImage(),
|
||||
child: const Text("이미지 삭제 (기본값 사용)", style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// 닉네임 입력
|
||||
TextField(
|
||||
controller: _nickController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "닉네임",
|
||||
border: OutlineInputBorder(),
|
||||
helperText: "게임에서 사용할 이름을 입력하세요.",
|
||||
),
|
||||
onChanged: (val) => _settings.setProfile(val, _settings.avatarIndex),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
// 기본 아바타 색상 선택 (이미지 없을 때 사용)
|
||||
const Align(alignment: Alignment.centerLeft, child: Text("기본 배경색")),
|
||||
const SizedBox(height: 5),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: List.generate(Colors.primaries.length, (index) {
|
||||
final isSelected = _settings.avatarIndex == index;
|
||||
return GestureDetector(
|
||||
onTap: () => _settings.setProfile(_nickController.text, index),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.primaries[index],
|
||||
shape: BoxShape.circle,
|
||||
border: isSelected ? Border.all(color: Colors.black, width: 2) : null,
|
||||
),
|
||||
child: isSelected ? const Icon(Icons.check, size: 16, color: Colors.white) : null,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// 2. 디스플레이 설정 섹션
|
||||
_buildSectionTitle("화면 설정"),
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SwitchListTile(
|
||||
title: const Text("다크 모드"),
|
||||
value: _settings.isDarkMode,
|
||||
onChanged: (val) => _settings.toggleDarkMode(val),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
const Text("글자 크기", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Slider(
|
||||
value: _settings.fontScale,
|
||||
min: 0.8,
|
||||
max: 1.5,
|
||||
divisions: 7,
|
||||
label: "${(_settings.fontScale * 100).toInt()}%",
|
||||
onChanged: (val) => _settings.setFontScale(val),
|
||||
),
|
||||
Text(
|
||||
"이 크기로 보입니다.",
|
||||
style: TextStyle(fontSize: 16 * _settings.fontScale),
|
||||
),
|
||||
const Divider(),
|
||||
|
||||
const Text("테마 색상", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: appColors.entries.map((entry) {
|
||||
final isSelected = _settings.themeColorName == entry.key;
|
||||
return GestureDetector(
|
||||
onTap: () => _settings.setThemeColor(entry.key),
|
||||
child: Container(
|
||||
width: 40, height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: entry.value,
|
||||
shape: BoxShape.circle,
|
||||
border: isSelected ? Border.all(color: Colors.black, width: 3) : null,
|
||||
boxShadow: [if(isSelected) const BoxShadow(blurRadius: 5, color: Colors.black26)],
|
||||
),
|
||||
child: isSelected ? const Icon(Icons.check, color: Colors.white) : null,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSectionTitle(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8, bottom: 8),
|
||||
child: Text(title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.grey)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,24 @@ import Foundation
|
||||
|
||||
import audioplayers_darwin
|
||||
import bonsoir_darwin
|
||||
import file_picker
|
||||
import file_selector_macos
|
||||
import flutter_local_notifications
|
||||
import gal
|
||||
import mobile_scanner
|
||||
import shared_preferences_foundation
|
||||
import speech_to_text
|
||||
import sqlite3_flutter_libs
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
SwiftBonsoirPlugin.register(with: registry.registrar(forPlugin: "SwiftBonsoirPlugin"))
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
|
||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SpeechToTextPlugin.register(with: registry.registrar(forPlugin: "SpeechToTextPlugin"))
|
||||
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
|
||||
}
|
||||
|
||||
+138
-2
@@ -229,10 +229,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "1bbf65dd997458a08b531042ec3794112a6c39c07c37ff22113d2e7e4f81d4e4"
|
||||
sha256: ab13ae8ef5580a411c458d6207b6774a6c237d77ac37011b13994879f68a8810
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.1"
|
||||
version: "8.3.7"
|
||||
file_selector_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -286,6 +286,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_local_notifications:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications
|
||||
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "17.2.4"
|
||||
flutter_local_notifications_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_linux
|
||||
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.1"
|
||||
flutter_local_notifications_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_local_notifications_platform_interface
|
||||
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -304,6 +328,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
gal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: gal
|
||||
sha256: "969598f986789127fd407a750413249e1352116d4c2be66e81837ffeeaafdfee"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -384,6 +416,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -520,6 +560,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -630,6 +678,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "46a46fd64659eff15f4638bbe19de43f9483f0e0bf024a9fb6b3582064bacc7b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.17"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -643,6 +747,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
speech_to_text:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: speech_to_text
|
||||
sha256: c07557664974afa061f221d0d4186935bea4220728ea9446702825e8b988db04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.0"
|
||||
speech_to_text_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: speech_to_text_platform_interface
|
||||
sha256: a1935847704e41ee468aad83181ddd2423d0833abe55d769c59afca07adb5114
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
speech_to_text_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: speech_to_text_windows
|
||||
sha256: "2c9846d18253c7bbe059a276297ef9f27e8a2745dead32192525beb208195072"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0+beta.8"
|
||||
sqlite3:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -707,6 +835,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.6"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timezone
|
||||
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
#include <bonsoir_windows/bonsoir_windows_plugin_c_api.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <gal/gal_plugin_c_api.h>
|
||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||
#include <speech_to_text_windows/speech_to_text_windows.h>
|
||||
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
@@ -19,8 +21,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("BonsoirWindowsPluginCApi"));
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||
GalPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("GalPluginCApi"));
|
||||
PermissionHandlerWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||
SpeechToTextWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("SpeechToTextWindows"));
|
||||
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
bonsoir_windows
|
||||
file_selector_windows
|
||||
gal
|
||||
permission_handler_windows
|
||||
speech_to_text_windows
|
||||
sqlite3_flutter_libs
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user