From 0ade16350a28c12a9f40de4bbcdfa635941030f5 Mon Sep 17 00:00:00 2001 From: soragui Date: Mon, 19 Jan 2026 14:50:21 +0800 Subject: [PATCH] feat: add auth screens and state management --- lib/app.dart | 28 ++++ lib/auth/auth_state.dart | 18 +++ lib/auth/phone_input_screen.dart | 33 ++++ lib/auth/sms_verification_screen.dart | 35 ++++ lib/main.dart | 9 ++ macos/Flutter/GeneratedPluginRegistrant.swift | 4 + pubspec.lock | 151 +++++++++++++++++- pubspec.yaml | 6 + .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 10 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 lib/app.dart create mode 100644 lib/auth/auth_state.dart create mode 100644 lib/auth/phone_input_screen.dart create mode 100644 lib/auth/sms_verification_screen.dart create mode 100644 lib/main.dart diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..8c27d90 --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:phone_login/auth/auth_state.dart'; +import 'package:phone_login/auth/phone_input_screen.dart'; +import 'package:phone_login/auth/sms_verification_screen.dart'; +import 'package:provider/provider.dart'; + +final _router = GoRouter( + routes: [ + GoRoute(path: '/', builder: (context, state) => const PhoneInputScreen()), + GoRoute( + path: '/sms_verify', + builder: (context, state) => const SmsVerificationScreen(), + ), + ], +); + +class App extends StatelessWidget { + const App({super.key}); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (_) => AuthState(), + child: MaterialApp.router(routerConfig: _router), + ); + } +} diff --git a/lib/auth/auth_state.dart b/lib/auth/auth_state.dart new file mode 100644 index 0000000..ae24c8e --- /dev/null +++ b/lib/auth/auth_state.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +class AuthState extends ChangeNotifier { + User? _user; + User? get user => _user; + + final FirebaseAuth _auth = FirebaseAuth.instance; + + AuthState() { + _auth.authStateChanges().listen((user) { + _user = user; + notifyListeners(); + }); + } + + bool get isLoggedIn => _user != null; +} diff --git a/lib/auth/phone_input_screen.dart b/lib/auth/phone_input_screen.dart new file mode 100644 index 0000000..4cfbf08 --- /dev/null +++ b/lib/auth/phone_input_screen.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:intl_phone_field/intl_phone_field.dart'; + +class PhoneInputScreen extends StatelessWidget { + const PhoneInputScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Enter Phone Number')), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + IntlPhoneField( + decoration: const InputDecoration( + labelText: 'Phone Number', + border: OutlineInputBorder(borderSide: BorderSide()), + ), + initialCountryCode: 'US', + onChanged: (phone) { + // TODO: Handle phone number changes + }, + ), + const SizedBox(height: 20), + ElevatedButton(onPressed: () {}, child: const Text('Send OTP')), + ], + ), + ), + ); + } +} diff --git a/lib/auth/sms_verification_screen.dart b/lib/auth/sms_verification_screen.dart new file mode 100644 index 0000000..7eaccec --- /dev/null +++ b/lib/auth/sms_verification_screen.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:pinput/pinput.dart'; + +class SmsVerificationScreen extends StatelessWidget { + const SmsVerificationScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('SMS Verification')), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Text( + 'Enter the 6-digit code sent to you', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 16), + ), + const SizedBox(height: 20), + Pinput( + length: 6, + onCompleted: (pin) { + // TODO: Handle OTP completion + }, + ), + const SizedBox(height: 20), + ElevatedButton(onPressed: () {}, child: const Text('Verify')), + ], + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..b423823 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,9 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/material.dart'; +import 'package:phone_login/app.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + runApp(const App()); +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..7b9be20 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import firebase_auth +import firebase_core func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index befc80c..d92a049 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: e4a1b612fd2955908e26116075b3a4baf10c353418ca645b4deae231c82bf144 + url: "https://pub.dev" + source: hosted + version: "1.3.65" async: dependency: transitive description: @@ -49,6 +57,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: "060a9bfc9877538dfdc69f2fdc1e0bf068439a7e9f22da6513f7b9bde308bb93" + url: "https://pub.dev" + source: hosted + version: "6.1.3" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: e91c9aadf9944e8319855fa752fd6c664bade2bbd6e7697a5142113c319a4288 + url: "https://pub.dev" + source: hosted + version: "8.1.5" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: "155145fd84f311e50eb2bfeb892f74b0d1b30936f40765a051b70110270cb6d1" + url: "https://pub.dev" + source: hosted + version: "6.1.1" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "29cfa93c771d8105484acac340b5ea0835be371672c91405a300303986f4eba9" + url: "https://pub.dev" + source: hosted + version: "4.3.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: a631bbfbfa26963d68046aed949df80b228964020e9155b086eff94f462bbf1f + url: "https://pub.dev" + source: hosted + version: "3.3.1" flutter: dependency: "direct main" description: flutter @@ -67,6 +123,43 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: eff94d2a6fc79fa8b811dde79c7549808c2346037ee107a1121b4a644c745f2a + url: "https://pub.dev" + source: hosted + version: "17.0.1" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl_phone_field: + dependency: "direct main" + description: + name: intl_phone_field + sha256: "73819d3dfcb68d2c85663606f6842597c3ddf6688ac777f051b17814fe767bbf" + url: "https://pub.dev" + source: hosted + version: "3.2.0" leak_tracker: dependency: transitive description: @@ -99,6 +192,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -123,6 +224,14 @@ packages: 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: @@ -131,6 +240,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "692e1c29703abefad6c502e97b4d946d506384397438ea242afadbfe48354819" + url: "https://pub.dev" + source: hosted + version: "6.0.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" sky_engine: dependency: transitive description: flutter @@ -184,6 +317,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.7" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -200,6 +341,14 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" sdks: dart: ">=3.10.4 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index b8895c3..355f909 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,12 @@ environment: dependencies: flutter: sdk: flutter + firebase_core: ^4.3.0 + firebase_auth: ^6.1.3 + go_router: ^17.0.1 + provider: ^6.1.5+1 + intl_phone_field: ^3.2.0 + pinput: ^6.0.1 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..d141b74 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,12 @@ #include "generated_plugin_registrant.h" +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..29944d5 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + firebase_auth + firebase_core ) list(APPEND FLUTTER_FFI_PLUGIN_LIST