diff --git a/.gitignore b/.gitignore
index dadd85f..0de20bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,8 +48,10 @@ app.*.map.json
/android/app/profile
/android/app/release
-# macOS (not targeting this platform)
+# Desktop platforms (not targeting these)
+linux/
macos/
+windows/
# Claude Code local settings
.claude/settings.local.json
diff --git a/assets/images/.gitkeep b/assets/images/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/assets/logo/coves_bubble.svg b/assets/logo/coves_bubble.svg
deleted file mode 100644
index a28701b..0000000
--- a/assets/logo/coves_bubble.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/assets/logo/coves_logo_text.svg b/assets/logo/coves_logo_text.svg
new file mode 100644
index 0000000..ed4836f
--- /dev/null
+++ b/assets/logo/coves_logo_text.svg
@@ -0,0 +1,80 @@
+
diff --git a/lib/constants/app_colors.dart b/lib/constants/app_colors.dart
index 97ddb6b..ddfe6e8 100644
--- a/lib/constants/app_colors.dart
+++ b/lib/constants/app_colors.dart
@@ -2,33 +2,102 @@ import 'package:flutter/material.dart';
/// Application color constants
///
-/// Centralized color definitions for consistent theming.
-/// Using semantic names improves maintainability.
+/// Coves design system - warm beach-inspired palette adapted for dark mode.
+/// Uses coral and teal as primary accents with warm undertones.
class AppColors {
// Private constructor to prevent instantiation
AppColors._();
- /// Primary background color - dark navy
+ // ═══════════════════════════════════════════════════════════════════════════
+ // BACKGROUNDS
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Primary background - deep ocean navy
static const background = Color(0xFF0B0F14);
- /// Primary accent color - orange/coral
- static const primary = Color(0xFFFF6B35);
+ /// Secondary background - elevated surfaces, cards
+ static const backgroundSecondary = Color(0xFF1A1F26);
+
+ /// Tertiary background - input fields, subtle elevation
+ static const backgroundTertiary = Color(0xFF1A2028);
+
+ // ═══════════════════════════════════════════════════════════════════════════
+ // BRAND COLORS
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Primary coral - warm orange (main accent)
+ static const coral = Color(0xFFF08C59);
+
+ /// Coral light - hover/glow states
+ static const coralLight = Color(0xFFFFB468);
+
+ /// Coral dark - pressed states
+ static const coralDark = Color(0xFFDF7E40);
- /// Secondary text color - light gray/blue
+ /// Teal - secondary brand color (ocean-inspired)
+ static const teal = Color(0xFF63B5B1);
+
+ /// Teal dark - secondary pressed states
+ static const tealDark = Color(0xFF4A9994);
+
+ /// Legacy primary (alias to coral for compatibility)
+ static const primary = coral;
+
+ // ═══════════════════════════════════════════════════════════════════════════
+ // TEXT COLORS
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Primary text - warm off-white
+ static const textPrimary = Color(0xFFFFF8F0);
+
+ /// Secondary text - muted cool gray (blue undertone)
static const textSecondary = Color(0xFFB6C2D2);
- /// Border color - dark gray
+ /// Muted text - subtle hints, placeholders
+ static const textMuted = Color(0xFF5A6B70);
+
+ /// Link text - teal accent
+ static const textLink = teal;
+
+ // ═══════════════════════════════════════════════════════════════════════════
+ // UI ELEMENTS
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Border color - subtle dark gray
static const border = Color(0xFF2A2F36);
- /// Secondary background color - slightly lighter than main
- static const backgroundSecondary = Color(0xFF1A1F26);
+ /// Border focused - coral accent
+ static const borderFocused = coral;
- /// Loading indicator color - medium gray
+ /// Loading indicator
static const loadingIndicator = Color(0xFF484F58);
- /// Off-white color for primary text
- static const textPrimary = Color(0xFFe4e6e7);
-
- /// Community name color - light blue/cyan
+ /// Community name - light cyan/blue
static const communityName = Color(0xFF7CB9E8);
+
+ // ═══════════════════════════════════════════════════════════════════════════
+ // SEMANTIC COLORS
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Success green
+ static const success = Color(0xFF22C55E);
+
+ /// Warning orange (uses coral)
+ static const warning = coralDark;
+
+ /// Error red
+ static const error = Color(0xFFEC7558);
+
+ // ═══════════════════════════════════════════════════════════════════════════
+ // DECORATIVE
+ // ═══════════════════════════════════════════════════════════════════════════
+
+ /// Floating bubble gradient start
+ static const bubbleGradientStart = Color(0x26F08C59); // coral @ ~15%
+
+ /// Floating bubble gradient end
+ static const bubbleGradientEnd = Color(0x2663B5B1); // teal @ ~15%
+
+ /// Ocean gradient overlay
+ static const oceanGradient = Color(0x4063B5B1); // teal @ ~25%
}
diff --git a/lib/main.dart b/lib/main.dart
index 870fe3f..74bc427 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,8 +1,11 @@
+import 'dart:async';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'config/oauth_config.dart';
import 'constants/app_colors.dart';
@@ -22,99 +25,126 @@ import 'services/streamable_service.dart';
import 'services/vote_service.dart';
import 'widgets/loading_error_states.dart';
-void main() async {
- WidgetsFlutterBinding.ensureInitialized();
+Future main() async {
+ await SentryFlutter.init(
+ (options) {
+ // TODO: Replace with your actual Sentry DSN from sentry.io
+ options.dsn = const String.fromEnvironment(
+ 'SENTRY_DSN',
+ defaultValue: '',
+ );
+ options.tracesSampleRate = kDebugMode ? 1.0 : 0.2;
+ options.environment = kDebugMode ? 'development' : 'production';
+ options.sendDefaultPii = false;
+ options.attachScreenshot = true;
+ options.attachViewHierarchy = true;
+ },
+ appRunner: () async {
+ WidgetsFlutterBinding.ensureInitialized();
- // Set system UI overlay style (Android navigation bar)
- SystemChrome.setSystemUIOverlayStyle(
- const SystemUiOverlayStyle(
- systemNavigationBarColor: Color(0xFF0B0F14),
- systemNavigationBarIconBrightness: Brightness.light,
- ),
- );
+ // Set system UI overlay style (Android navigation bar)
+ SystemChrome.setSystemUIOverlayStyle(
+ const SystemUiOverlayStyle(
+ systemNavigationBarColor: Color(0xFF0B0F14),
+ systemNavigationBarIconBrightness: Brightness.light,
+ ),
+ );
- // Initialize auth provider
- final authProvider = AuthProvider();
- await authProvider.initialize();
-
- // Initialize vote service with auth callbacks
- // Votes go through the Coves backend (which proxies to PDS with DPoP)
- // Includes token refresh and sign-out handlers for automatic 401 recovery
- final voteService = VoteService(
- sessionGetter: () async => authProvider.session,
- didGetter: () => authProvider.did,
- tokenRefresher: authProvider.refreshToken,
- signOutHandler: authProvider.signOut,
- );
+ // Initialize auth provider
+ final authProvider = AuthProvider();
+ try {
+ await authProvider.initialize();
+ } on Exception catch (error, stackTrace) {
+ // Log initialization failure but continue - user can retry login
+ await Sentry.captureException(
+ error,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('phase', 'auth_initialization');
+ },
+ );
+ }
- // Initialize comment service with auth callbacks
- // Comments go through the Coves backend (which proxies to PDS with DPoP)
- final commentService = CommentService(
- sessionGetter: () async => authProvider.session,
- tokenRefresher: authProvider.refreshToken,
- signOutHandler: authProvider.signOut,
- );
+ // Initialize vote service with auth callbacks
+ // Votes go through the Coves backend (which proxies to PDS with DPoP)
+ // Includes token refresh and sign-out handlers for automatic 401 recovery
+ final voteService = VoteService(
+ sessionGetter: () async => authProvider.session,
+ didGetter: () => authProvider.did,
+ tokenRefresher: authProvider.refreshToken,
+ signOutHandler: authProvider.signOut,
+ );
- runApp(
- MultiProvider(
- providers: [
- ChangeNotifierProvider.value(value: authProvider),
- ChangeNotifierProvider(
- create:
- (_) => VoteProvider(
- voteService: voteService,
- authProvider: authProvider,
- ),
- ),
- ChangeNotifierProxyProvider2<
- AuthProvider,
- VoteProvider,
- MultiFeedProvider
- >(
- create:
- (context) => MultiFeedProvider(
- authProvider,
- voteProvider: context.read(),
- ),
- update: (context, auth, vote, previous) {
- // Reuse existing provider to maintain state across rebuilds
- return previous ?? MultiFeedProvider(auth, voteProvider: vote);
- },
- ),
- // CommentsProviderCache manages per-post CommentsProvider instances
- // with LRU eviction and sign-out cleanup
- ProxyProvider2(
- create:
- (context) => CommentsProviderCache(
- authProvider: authProvider,
- voteProvider: context.read(),
- commentService: commentService,
- ),
- update: (context, auth, vote, previous) {
- // Reuse existing cache
- return previous ??
- CommentsProviderCache(
- authProvider: auth,
- voteProvider: vote,
- commentService: commentService,
- );
- },
- dispose: (_, cache) => cache.dispose(),
- ),
- // StreamableService for video embeds
- Provider(create: (_) => StreamableService()),
- // UserProfileProvider for profile pages
- ChangeNotifierProxyProvider(
- create: (context) => UserProfileProvider(authProvider),
- update: (context, auth, previous) {
- // Propagate auth changes to existing provider
- previous?.updateAuthProvider(auth);
- return previous ?? UserProfileProvider(auth);
- },
+ // Initialize comment service with auth callbacks
+ // Comments go through the Coves backend (which proxies to PDS with DPoP)
+ final commentService = CommentService(
+ sessionGetter: () async => authProvider.session,
+ tokenRefresher: authProvider.refreshToken,
+ signOutHandler: authProvider.signOut,
+ );
+
+ runApp(
+ MultiProvider(
+ providers: [
+ ChangeNotifierProvider.value(value: authProvider),
+ ChangeNotifierProvider(
+ create:
+ (_) => VoteProvider(
+ voteService: voteService,
+ authProvider: authProvider,
+ ),
+ ),
+ ChangeNotifierProxyProvider2<
+ AuthProvider,
+ VoteProvider,
+ MultiFeedProvider
+ >(
+ create:
+ (context) => MultiFeedProvider(
+ authProvider,
+ voteProvider: context.read(),
+ ),
+ update: (context, auth, vote, previous) {
+ // Reuse existing provider to maintain state across rebuilds
+ return previous ?? MultiFeedProvider(auth, voteProvider: vote);
+ },
+ ),
+ // CommentsProviderCache manages per-post CommentsProvider instances
+ // with LRU eviction and sign-out cleanup
+ ProxyProvider2(
+ create:
+ (context) => CommentsProviderCache(
+ authProvider: authProvider,
+ voteProvider: context.read(),
+ commentService: commentService,
+ ),
+ update: (context, auth, vote, previous) {
+ // Reuse existing cache
+ return previous ??
+ CommentsProviderCache(
+ authProvider: auth,
+ voteProvider: vote,
+ commentService: commentService,
+ );
+ },
+ dispose: (_, cache) => cache.dispose(),
+ ),
+ // StreamableService for video embeds
+ Provider(create: (_) => StreamableService()),
+ // UserProfileProvider for profile pages
+ ChangeNotifierProxyProvider(
+ create: (context) => UserProfileProvider(authProvider),
+ update: (context, auth, previous) {
+ // Propagate auth changes to existing provider
+ previous?.updateAuthProvider(auth);
+ return previous ?? UserProfileProvider(auth);
+ },
+ ),
+ ],
+ child: const CovesApp(),
),
- ],
- child: const CovesApp(),
- ),
+ );
+ },
);
}
diff --git a/lib/screens/auth/login_screen.dart b/lib/screens/auth/login_screen.dart
index 7c5ed3b..e1dbfb6 100644
--- a/lib/screens/auth/login_screen.dart
+++ b/lib/screens/auth/login_screen.dart
@@ -1,12 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
+import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import '../../constants/app_colors.dart';
import '../../providers/auth_provider.dart';
import '../../widgets/primary_button.dart';
+/// Login screen with Coves design language.
+///
+/// Features the warm beach-inspired aesthetic with custom typography
+/// and refined input styling.
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@@ -17,11 +23,24 @@ class LoginScreen extends StatefulWidget {
class _LoginScreenState extends State {
final _handleController = TextEditingController();
final _formKey = GlobalKey();
+ final _focusNode = FocusNode();
bool _isLoading = false;
+ bool _isFocused = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _focusNode.addListener(_onFocusChange);
+ }
+
+ void _onFocusChange() {
+ setState(() => _isFocused = _focusNode.hasFocus);
+ }
@override
void dispose() {
_handleController.dispose();
+ _focusNode.dispose();
super.dispose();
}
@@ -29,22 +48,88 @@ class _LoginScreenState extends State {
showDialog(
context: context,
builder: (context) => AlertDialog(
- backgroundColor: const Color(0xFF1A2028),
- title: const Text(
+ backgroundColor: AppColors.backgroundSecondary,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(20),
+ side: BorderSide(
+ color: AppColors.border,
+ width: 1,
+ ),
+ ),
+ title: Text(
'What is a handle?',
- style: TextStyle(color: Colors.white),
+ style: GoogleFonts.nunito(
+ color: AppColors.textPrimary,
+ fontWeight: FontWeight.w700,
+ fontSize: 20,
+ ),
),
- content: const Text(
- 'Your handle is your unique identifier '
- 'on the atproto network, like '
- 'alice.bsky.social. If you don\'t have one '
- 'yet, you can create an account at bsky.app.',
- style: TextStyle(color: AppColors.textSecondary),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Your handle is your unique identifier on the AT Protocol network.',
+ style: GoogleFonts.nunito(
+ color: AppColors.textSecondary,
+ fontSize: 15,
+ height: 1.5,
+ ),
+ ),
+ const SizedBox(height: 16),
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: AppColors.teal.withValues(alpha: 0.1),
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(
+ color: AppColors.teal.withValues(alpha: 0.3),
+ ),
+ ),
+ child: Row(
+ children: [
+ Icon(
+ Icons.alternate_email,
+ color: AppColors.teal,
+ size: 20,
+ ),
+ const SizedBox(width: 10),
+ Text(
+ 'alice.bsky.social',
+ style: GoogleFonts.nunito(
+ color: AppColors.teal,
+ fontWeight: FontWeight.w600,
+ fontSize: 14,
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'If you don\'t have one yet, you can create an account at bsky.app.',
+ style: GoogleFonts.nunito(
+ color: AppColors.textMuted,
+ fontSize: 13,
+ height: 1.5,
+ ),
+ ),
+ ],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
- child: const Text('Got it'),
+ style: TextButton.styleFrom(
+ foregroundColor: AppColors.coral,
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
+ ),
+ child: Text(
+ 'Got it',
+ style: GoogleFonts.nunito(
+ fontWeight: FontWeight.w700,
+ fontSize: 15,
+ ),
+ ),
),
],
),
@@ -63,35 +148,60 @@ class _LoginScreenState extends State {
await authProvider.signIn(_handleController.text.trim());
if (mounted) {
- // Navigate to feed on successful login
context.go('/feed');
}
- } on Exception catch (e) {
- if (mounted) {
- final errorString = e.toString().toLowerCase();
- String userMessage;
- if (errorString.contains('timeout') ||
- errorString.contains('socketexception') ||
- errorString.contains('connection')) {
- userMessage =
- 'Network error. Please check your connection and try again.';
- } else if (errorString.contains('404') ||
- errorString.contains('not found')) {
- userMessage =
- 'Handle not found. Please verify your handle is correct.';
- } else if (errorString.contains('401') ||
- errorString.contains('403') ||
- errorString.contains('unauthorized')) {
- userMessage = 'Authorization failed. Please try again.';
- } else {
- userMessage = 'Sign in failed. Please try again later.';
- debugPrint('Sign in error: $e');
- }
+ } on Exception catch (e, stackTrace) {
+ // Log all sign-in errors to Sentry with categorization
+ final errorString = e.toString().toLowerCase();
+ String userMessage;
+ String errorCategory;
+
+ if (errorString.contains('timeout') ||
+ errorString.contains('socketexception') ||
+ errorString.contains('connection')) {
+ userMessage =
+ 'Network error. Please check your connection and try again.';
+ errorCategory = 'network';
+ } else if (errorString.contains('404') ||
+ errorString.contains('not found')) {
+ userMessage =
+ 'Handle not found. Please verify your handle is correct.';
+ errorCategory = 'not_found';
+ } else if (errorString.contains('401') ||
+ errorString.contains('403') ||
+ errorString.contains('unauthorized')) {
+ userMessage = 'Authorization failed. Please try again.';
+ errorCategory = 'auth_failure';
+ } else {
+ userMessage = 'Sign in failed. Please try again later.';
+ errorCategory = 'unexpected';
+ }
+ Sentry.captureException(
+ e,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('error_category', errorCategory);
+ scope.setTag('screen', 'login');
+ scope.setTag('action', 'sign_in');
+ scope.setContexts('sign_in', {
+ 'handle_provided': _handleController.text.isNotEmpty,
+ });
+ },
+ );
+
+ if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
- content: Text(userMessage),
- backgroundColor: Colors.red[700],
+ content: Text(
+ userMessage,
+ style: GoogleFonts.nunito(fontWeight: FontWeight.w500),
+ ),
+ backgroundColor: AppColors.error,
+ behavior: SnackBarBehavior.floating,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
),
);
}
@@ -104,164 +214,380 @@ class _LoginScreenState extends State {
@override
Widget build(BuildContext context) {
+ final screenHeight = MediaQuery.of(context).size.height;
+
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) {
if (!didPop) {
- context.go('/');
+ context.pop();
}
},
child: Scaffold(
backgroundColor: AppColors.background,
resizeToAvoidBottomInset: false,
- appBar: AppBar(
- backgroundColor: AppColors.background,
- foregroundColor: Colors.white,
- elevation: 0,
- leading: IconButton(
- icon: const Icon(Icons.arrow_back),
- onPressed: () => context.go('/'),
- ),
- ),
- body: GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => FocusScope.of(context).unfocus(),
- child: SafeArea(
- child: Padding(
- padding: const EdgeInsets.all(24),
- child: Form(
- key: _formKey,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- const SizedBox(height: 32),
-
- // Title
- const Text(
- 'Enter your atproto handle',
- style: TextStyle(
- fontSize: 24,
- color: Colors.white,
- fontWeight: FontWeight.bold,
- ),
- textAlign: TextAlign.center,
- ),
+ body: Stack(
+ children: [
+ // Ocean gradient background
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ height: screenHeight * 0.35,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Colors.transparent,
+ AppColors.teal.withValues(alpha: 0.06),
+ AppColors.teal.withValues(alpha: 0.12),
+ ],
+ ),
+ ),
+ ),
+ ),
- const SizedBox(height: 12),
-
- // Provider logos
- Center(
- child: SvgPicture.asset(
- 'assets/icons/atproto/providers_stack.svg',
- height: 24,
- errorBuilder: (context, error, stackTrace) {
- debugPrint(
- 'Failed to load providers_stack.svg: $error',
- );
- return const SizedBox(height: 24);
- },
- ),
- ),
+ // Main content
+ SafeArea(
+ child: Column(
+ children: [
+ // App bar
+ _buildAppBar(context),
- const SizedBox(height: 32),
-
- // Handle input field
- TextFormField(
- controller: _handleController,
- enabled: !_isLoading,
- style: const TextStyle(color: Colors.white),
- decoration: InputDecoration(
- hintText: 'alice.bsky.social',
- hintStyle: const TextStyle(color: Color(0xFF5A6B7F)),
- filled: true,
- fillColor: const Color(0xFF1A2028),
- border: OutlineInputBorder(
- borderRadius: BorderRadius.circular(12),
- borderSide: const BorderSide(
- color: Color(0xFF2A3441),
- ),
- ),
- enabledBorder: OutlineInputBorder(
- borderRadius: BorderRadius.circular(12),
- borderSide: const BorderSide(
- color: Color(0xFF2A3441),
- ),
- ),
- focusedBorder: OutlineInputBorder(
- borderRadius: BorderRadius.circular(12),
- borderSide: const BorderSide(
- color: AppColors.primary,
- width: 2,
- ),
- ),
- prefixIcon: const Padding(
- padding: EdgeInsets.only(left: 16, right: 8),
- child: Text(
- '@',
- style: TextStyle(
- color: Color(0xFF5A6B7F),
- fontSize: 18,
- fontWeight: FontWeight.w500,
- ),
+ // Scrollable content
+ Expanded(
+ child: GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onTap: () => FocusScope.of(context).unfocus(),
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Form(
+ key: _formKey,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ const SizedBox(height: 32),
+
+ // Title with Shrikhand display font
+ _buildTitle(),
+
+ const SizedBox(height: 32),
+
+ // Handle input field
+ _buildHandleInput(),
+
+ const SizedBox(height: 28),
+
+ // Sign in button
+ Center(
+ child: PrimaryButton(
+ title:
+ _isLoading ? 'Signing in...' : 'Sign in',
+ onPressed: _isLoading ? () {} : _handleSignIn,
+ disabled: _isLoading,
+ ),
+ ),
+
+ const SizedBox(height: 24),
+
+ // Info text
+ _buildInfoText(),
+
+ const SizedBox(height: 40),
+
+ // Help link
+ _buildHelpLink(),
+
+ const SizedBox(height: 32),
+ ],
),
),
- prefixIconConstraints: const BoxConstraints(),
),
- keyboardType: TextInputType.emailAddress,
- autocorrect: false,
- textInputAction: TextInputAction.done,
- onFieldSubmitted: (_) => _handleSignIn(),
- validator: (value) {
- if (value == null || value.trim().isEmpty) {
- return 'Please enter your handle';
- }
- // Basic handle validation
- if (!value.contains('.')) {
- return 'Handle must contain a domain '
- '(e.g., user.bsky.social)';
- }
- return null;
- },
),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ Widget _buildAppBar(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
+ child: Row(
+ children: [
+ IconButton(
+ icon: Icon(
+ Icons.arrow_back_rounded,
+ color: AppColors.textSecondary,
+ ),
+ onPressed: () => context.pop(),
+ style: IconButton.styleFrom(
+ backgroundColor: AppColors.backgroundSecondary,
+ padding: const EdgeInsets.all(12),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
- const SizedBox(height: 32),
+ Widget _buildTitle() {
+ return Column(
+ children: [
+ // Decorative line
+ Container(
+ width: 40,
+ height: 3,
+ decoration: BoxDecoration(
+ color: AppColors.coral,
+ borderRadius: BorderRadius.circular(2),
+ ),
+ ),
+ const SizedBox(height: 20),
- // Sign in button
- PrimaryButton(
- title: _isLoading ? 'Signing in...' : 'Sign In',
- onPressed: _isLoading ? () {} : _handleSignIn,
- disabled: _isLoading,
- ),
+ Text(
+ 'Welcome back',
+ style: GoogleFonts.shrikhand(
+ fontSize: 28,
+ color: AppColors.textPrimary,
+ height: 1.2,
+ ),
+ textAlign: TextAlign.center,
+ ),
- const SizedBox(height: 24),
+ const SizedBox(height: 12),
- // Info text
- const Text(
- 'You\'ll be redirected to authorize this app with your '
- 'atproto provider.',
- style: TextStyle(fontSize: 14, color: Color(0xFF5A6B7F)),
- textAlign: TextAlign.center,
- ),
+ // Provider logos
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ 'Sign in with your atproto identity',
+ style: GoogleFonts.nunito(
+ fontSize: 15,
+ color: AppColors.textSecondary,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ],
+ ),
- const Spacer(),
-
- // Help text
- Center(
- child: TextButton(
- onPressed: _showHandleHelpDialog,
- child: const Text(
- 'What is a handle?',
- style: TextStyle(
- color: AppColors.primary,
- decoration: TextDecoration.underline,
- ),
- ),
- ),
+ const SizedBox(height: 12),
+
+ SvgPicture.asset(
+ 'assets/icons/atproto/providers_stack.svg',
+ height: 24,
+ errorBuilder: (context, error, stackTrace) {
+ Sentry.captureException(
+ error,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('asset', 'providers_stack.svg');
+ scope.setTag('screen', 'login');
+ },
+ );
+ return Text(
+ 'Bluesky \u2022 ATProto',
+ style: GoogleFonts.nunito(
+ fontSize: 12,
+ color: AppColors.textMuted,
+ fontWeight: FontWeight.w500,
+ ),
+ );
+ },
+ ),
+ ],
+ );
+ }
+
+ Widget _buildHandleInput() {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Label
+ Padding(
+ padding: const EdgeInsets.only(left: 4, bottom: 8),
+ child: Text(
+ 'YOUR HANDLE',
+ style: GoogleFonts.nunito(
+ fontSize: 11,
+ color: AppColors.textMuted,
+ fontWeight: FontWeight.w700,
+ letterSpacing: 1.2,
+ ),
+ ),
+ ),
+
+ // Input field with custom styling
+ AnimatedContainer(
+ duration: const Duration(milliseconds: 200),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(16),
+ boxShadow: _isFocused
+ ? [
+ BoxShadow(
+ color: AppColors.coral.withValues(alpha: 0.2),
+ blurRadius: 12,
+ spreadRadius: 0,
),
- ],
+ ]
+ : null,
+ ),
+ child: TextFormField(
+ controller: _handleController,
+ focusNode: _focusNode,
+ enabled: !_isLoading,
+ style: GoogleFonts.nunito(
+ color: AppColors.textPrimary,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ decoration: InputDecoration(
+ hintText: 'alice.bsky.social',
+ hintStyle: GoogleFonts.nunito(
+ color: AppColors.textMuted,
+ fontSize: 16,
+ ),
+ filled: true,
+ fillColor: AppColors.backgroundTertiary,
+ contentPadding: const EdgeInsets.symmetric(
+ horizontal: 20,
+ vertical: 18,
+ ),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(
+ color: AppColors.border,
+ width: 2,
+ ),
+ ),
+ enabledBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(
+ color: AppColors.border,
+ width: 2,
+ ),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(
+ color: AppColors.coral,
+ width: 2,
+ ),
+ ),
+ errorBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(
+ color: AppColors.error,
+ width: 2,
+ ),
+ ),
+ focusedErrorBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(16),
+ borderSide: BorderSide(
+ color: AppColors.error,
+ width: 2,
+ ),
+ ),
+ errorStyle: GoogleFonts.nunito(
+ color: AppColors.error,
+ fontSize: 12,
+ ),
+ prefixIcon: Padding(
+ padding: const EdgeInsets.only(left: 20, right: 4),
+ child: Text(
+ '@',
+ style: GoogleFonts.nunito(
+ color: _isFocused ? AppColors.coral : AppColors.textMuted,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ ),
),
),
+ prefixIconConstraints: const BoxConstraints(),
),
+ keyboardType: TextInputType.emailAddress,
+ autocorrect: false,
+ textInputAction: TextInputAction.done,
+ onFieldSubmitted: (_) => _handleSignIn(),
+ validator: (value) {
+ if (value == null || value.trim().isEmpty) {
+ return 'Please enter your handle';
+ }
+ if (!value.contains('.')) {
+ return 'Handle must contain a domain (e.g., user.bsky.social)';
+ }
+ return null;
+ },
+ ),
+ ),
+ ],
+ );
+ }
+
+ Widget _buildInfoText() {
+ return Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ color: AppColors.teal.withValues(alpha: 0.08),
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(
+ color: AppColors.teal.withValues(alpha: 0.2),
+ ),
+ ),
+ child: Row(
+ children: [
+ Icon(
+ Icons.security_rounded,
+ color: AppColors.teal,
+ size: 20,
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Text(
+ 'You\'ll be redirected to authorize securely with your atproto provider.',
+ style: GoogleFonts.nunito(
+ fontSize: 13,
+ color: AppColors.textSecondary,
+ height: 1.4,
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildHelpLink() {
+ return Center(
+ child: GestureDetector(
+ onTap: _showHandleHelpDialog,
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ Icons.help_outline_rounded,
+ color: AppColors.coral,
+ size: 18,
+ ),
+ const SizedBox(width: 8),
+ Text(
+ 'What is a handle?',
+ style: GoogleFonts.nunito(
+ color: AppColors.coral,
+ fontWeight: FontWeight.w600,
+ fontSize: 14,
+ ),
+ ),
+ ],
),
),
),
diff --git a/lib/screens/landing_screen.dart b/lib/screens/landing_screen.dart
index 92ddba8..d64eeeb 100644
--- a/lib/screens/landing_screen.dart
+++ b/lib/screens/landing_screen.dart
@@ -1,126 +1,382 @@
+import 'dart:async' show unawaited;
+import 'dart:math' as math;
+
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart';
+import 'package:google_fonts/google_fonts.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import '../constants/app_colors.dart';
import '../widgets/primary_button.dart';
-class LandingScreen extends StatelessWidget {
+/// Landing screen with Coves beach-inspired dark theme design.
+///
+/// Clean, lightweight layout with floating decorative bubbles.
+class LandingScreen extends StatefulWidget {
const LandingScreen({super.key});
+ @override
+ State createState() => _LandingScreenState();
+}
+
+class _LandingScreenState extends State
+ with SingleTickerProviderStateMixin, WidgetsBindingObserver {
+ // Floating bubbles animation
+ late AnimationController _bubbleController;
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addObserver(this);
+ _bubbleController = AnimationController(
+ duration: const Duration(seconds: 8),
+ vsync: this,
+ )..repeat();
+ }
+
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ // Pause continuous animations when app is backgrounded to save battery
+ if (state == AppLifecycleState.paused ||
+ state == AppLifecycleState.inactive) {
+ _bubbleController.stop();
+ } else if (state == AppLifecycleState.resumed) {
+ if (!_bubbleController.isAnimating) {
+ _bubbleController.repeat();
+ }
+ }
+ }
+
+ @override
+ void dispose() {
+ WidgetsBinding.instance.removeObserver(this);
+ _bubbleController.dispose();
+ super.dispose();
+ }
+
@override
Widget build(BuildContext context) {
+ final screenHeight = MediaQuery.of(context).size.height;
+
return Scaffold(
backgroundColor: AppColors.background,
- body: SafeArea(
- child: Center(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 24),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- // Lil Dude character
- SvgPicture.asset(
- 'assets/logo/lil_dude.svg',
- width: 120,
- height: 120,
- errorBuilder: (context, error, stackTrace) {
- debugPrint('Failed to load lil_dude.svg: $error');
- return const SizedBox(width: 120, height: 120);
- },
- ),
- const SizedBox(height: 16),
-
- // Coves bubble text
- SvgPicture.asset(
- 'assets/logo/coves_bubble.svg',
- width: 180,
- height: 60,
- errorBuilder: (context, error, stackTrace) {
- debugPrint('Failed to load coves_bubble.svg: $error');
- return const SizedBox(width: 180, height: 60);
- },
- ),
+ body: Stack(
+ children: [
+ // Ocean gradient background
+ _buildOceanGradient(screenHeight),
- const SizedBox(height: 48),
+ // Floating bubbles
+ _buildFloatingBubbles(),
- // "Bring your @handle" with logos
- Row(
+ // Main content
+ SafeArea(
+ child: Center(
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
- const Text(
- 'Bring your atproto handle',
- style: TextStyle(
- fontSize: 14,
- color: Color(0xFF8A96A6),
- fontWeight: FontWeight.w500,
- ),
+ const SizedBox(height: 60),
+
+ // Brand lockup: Mascot + Logo
+ _buildBrandLockup(),
+
+ const SizedBox(height: 56),
+
+ // Buttons
+ _buildButtons(context),
+
+ const SizedBox(height: 48),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildOceanGradient(double screenHeight) {
+ return Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ height: screenHeight * 0.45,
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Colors.transparent,
+ AppColors.teal.withValues(alpha: 0.08),
+ AppColors.teal.withValues(alpha: 0.15),
+ ],
+ stops: const [0.0, 0.5, 1.0],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildFloatingBubbles() {
+ return AnimatedBuilder(
+ animation: _bubbleController,
+ builder: (context, child) {
+ return Stack(
+ children: [
+ _buildBubble(
+ left: 20,
+ bottom: 100,
+ size: 80,
+ color: AppColors.coral,
+ offset: 0.0,
+ ),
+ _buildBubble(
+ right: 30,
+ bottom: 200,
+ size: 60,
+ color: AppColors.teal,
+ offset: 0.25,
+ ),
+ _buildBubble(
+ left: 60,
+ top: 150,
+ size: 50,
+ color: AppColors.coralLight,
+ offset: 0.5,
+ ),
+ _buildBubble(
+ right: 50,
+ top: 250,
+ size: 70,
+ color: AppColors.tealDark,
+ offset: 0.75,
+ ),
+ ],
+ );
+ },
+ );
+ }
+
+ Widget _buildBubble({
+ double? left,
+ double? right,
+ double? top,
+ double? bottom,
+ required double size,
+ required Color color,
+ required double offset,
+ }) {
+ final progress = (_bubbleController.value + offset) % 1.0;
+ final yOffset = math.sin(progress * 2 * math.pi) * 20;
+
+ return Positioned(
+ left: left,
+ right: right,
+ top: top != null ? top + yOffset : null,
+ bottom: bottom != null ? bottom - yOffset : null,
+ child: Container(
+ width: size,
+ height: size,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ gradient: RadialGradient(
+ colors: [
+ color.withValues(alpha: 0.2),
+ color.withValues(alpha: 0.05),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildBrandLockup() {
+ return Column(
+ children: [
+ // Mascot
+ SvgPicture.asset(
+ 'assets/logo/lil_dude.svg',
+ width: 110,
+ height: 110,
+ errorBuilder: (context, error, stackTrace) {
+ unawaited(
+ Sentry.captureException(
+ error,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('asset', 'lil_dude.svg');
+ scope.setTag('screen', 'landing');
+ },
+ ),
+ );
+ return Container(
+ width: 110,
+ height: 110,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: AppColors.teal.withValues(alpha: 0.15),
+ ),
+ child: const Icon(
+ Icons.water_drop_rounded,
+ size: 48,
+ color: AppColors.teal,
+ ),
+ );
+ },
+ ),
+
+ const SizedBox(height: 16),
+
+ // Logo text
+ SvgPicture.asset(
+ 'assets/logo/coves_logo_text.svg',
+ height: 65,
+ errorBuilder: (context, error, stackTrace) {
+ unawaited(
+ Sentry.captureException(
+ error,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('asset', 'coves_logo_text.svg');
+ scope.setTag('screen', 'landing');
+ },
+ ),
+ );
+ return Text(
+ 'Coves',
+ style: GoogleFonts.shrikhand(
+ fontSize: 48,
+ color: AppColors.textPrimary,
+ ),
+ );
+ },
+ ),
+ ],
+ );
+ }
+
+ Widget _buildButtons(BuildContext context) {
+ return Column(
+ children: [
+ // Bring your handle text with provider logos
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ 'Bring your atproto handle',
+ style: GoogleFonts.nunito(
+ fontSize: 15,
+ color: AppColors.textSecondary,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ const SizedBox(width: 8),
+ SvgPicture.asset(
+ 'assets/icons/atproto/providers_landing.svg',
+ height: 18,
+ errorBuilder: (context, error, stackTrace) {
+ unawaited(
+ Sentry.captureException(
+ error,
+ stackTrace: stackTrace,
+ withScope: (scope) {
+ scope.setTag('asset', 'providers_landing.svg');
+ scope.setTag('screen', 'landing');
+ },
+ ),
+ );
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ Icons.cloud_outlined,
+ size: 16,
+ color: AppColors.textSecondary,
),
- const SizedBox(width: 8),
- SvgPicture.asset(
- 'assets/icons/atproto/providers_landing.svg',
- height: 18,
- errorBuilder: (context, error, stackTrace) {
- debugPrint(
- 'Failed to load providers_landing.svg: $error',
- );
- return const SizedBox(height: 18);
- },
+ const SizedBox(width: 4),
+ Icon(
+ Icons.cloud_outlined,
+ size: 16,
+ color: AppColors.textSecondary,
),
],
- ),
+ );
+ },
+ ),
+ ],
+ ),
- const SizedBox(height: 16),
+ const SizedBox(height: 20),
- // Sign in button
- PrimaryButton(
- title: 'Sign in',
- onPressed: () {
- context.go('/login');
- },
- ),
+ // Sign in button
+ PrimaryButton(
+ title: 'Sign in',
+ onPressed: () => context.push('/login'),
+ ),
+
+ const SizedBox(height: 14),
- const SizedBox(height: 12),
-
- // Create account button
- PrimaryButton(
- title: 'Create account',
- onPressed: () {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text('Account registration coming soon!'),
- duration: Duration(seconds: 2),
- ),
- );
- },
- variant: ButtonVariant.outline,
+ // Create account button
+ PrimaryButton(
+ title: 'Create account',
+ variant: ButtonVariant.outline,
+ onPressed: () {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(
+ 'Account creation coming soon!',
+ style: GoogleFonts.nunito(
+ fontWeight: FontWeight.w600,
+ color: AppColors.background,
+ ),
+ ),
+ backgroundColor: AppColors.coral,
+ behavior: SnackBarBehavior.floating,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
),
+ ),
+ );
+ },
+ ),
- const SizedBox(height: 20),
-
- // Explore link
- GestureDetector(
- onTap: () {
- context.go('/feed');
- },
- child: const Padding(
- padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12),
- child: Text(
- 'Explore our communities!',
- style: TextStyle(
- fontSize: 14,
- color: Color(0xFF8A96A6),
- fontWeight: FontWeight.w500,
- decoration: TextDecoration.underline,
- ),
- ),
+ const SizedBox(height: 24),
+
+ // Explore link
+ GestureDetector(
+ onTap: () => context.go('/feed'),
+ child: Container(
+ padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(20),
+ color: AppColors.teal.withValues(alpha: 0.1),
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(
+ Icons.explore_outlined,
+ size: 18,
+ color: AppColors.teal,
+ ),
+ const SizedBox(width: 8),
+ Text(
+ 'Explore communities',
+ style: GoogleFonts.nunito(
+ fontSize: 14,
+ color: AppColors.teal,
+ fontWeight: FontWeight.w600,
),
),
],
),
),
),
- ),
+ ],
);
}
}
diff --git a/lib/widgets/primary_button.dart b/lib/widgets/primary_button.dart
index 87ad683..61dd350 100644
--- a/lib/widgets/primary_button.dart
+++ b/lib/widgets/primary_button.dart
@@ -1,55 +1,100 @@
import 'package:flutter/material.dart';
+import 'package:google_fonts/google_fonts.dart';
import '../constants/app_colors.dart';
enum ButtonVariant { solid, outline, tertiary }
-class PrimaryButton extends StatelessWidget {
+/// Primary button with Coves design language.
+///
+/// Features warm coral accent with refined styling across all variants.
+///
+/// ## Variants
+/// - [ButtonVariant.solid]: Filled coral background with subtle shadow.
+/// Use for primary actions (e.g., "Sign in", "Submit").
+/// - [ButtonVariant.outline]: Transparent with coral border. Use for secondary
+/// actions that still need prominence (e.g., "Create account").
+/// - [ButtonVariant.tertiary]: Subtle gray background. Use for less important
+/// actions (e.g., "Cancel", "Skip").
+///
+/// ## Icon Parameter
+/// Optional widget displayed before the title. Typically an [Icon] widget.
+/// The icon inherits the button's text color for consistency.
+class PrimaryButton extends StatefulWidget {
const PrimaryButton({
super.key,
required this.title,
required this.onPressed,
this.variant = ButtonVariant.solid,
this.disabled = false,
+ this.icon,
});
+
final String title;
final VoidCallback onPressed;
final ButtonVariant variant;
final bool disabled;
+ /// Optional icon widget displayed before the title text.
+ final Widget? icon;
+
+ @override
+ State createState() => _PrimaryButtonState();
+}
+
+class _PrimaryButtonState extends State {
+ bool _isPressed = false;
+
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.of(context).size.width * 0.75,
- height: 48,
- child: ElevatedButton(
- onPressed: disabled ? null : onPressed,
- style: ElevatedButton.styleFrom(
- backgroundColor: _getBackgroundColor(),
- foregroundColor: _getTextColor(),
- disabledBackgroundColor: _getBackgroundColor().withValues(alpha: 0.5),
- disabledForegroundColor: _getTextColor().withValues(alpha: 0.5),
- overlayColor: _getOverlayColor(),
- splashFactory: NoSplash.splashFactory,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(9999),
- side: _getBorderSide(),
+ height: 52,
+ child: GestureDetector(
+ onTapDown: (_) => setState(() => _isPressed = true),
+ onTapUp: (_) => setState(() => _isPressed = false),
+ onTapCancel: () => setState(() => _isPressed = false),
+ child: AnimatedContainer(
+ duration: const Duration(milliseconds: 150),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(100),
+ color: _getBackgroundColor(),
+ border: _getBorder(),
+ boxShadow: _getBoxShadow(),
),
- elevation: variant == ButtonVariant.solid ? 8 : 0,
- shadowColor:
- variant == ButtonVariant.solid
- ? AppColors.primary.withValues(alpha: 0.4)
- : Colors.transparent,
- padding: const EdgeInsets.symmetric(vertical: 12),
- ),
- child: Text(
- title,
- style: TextStyle(
- fontSize: variant == ButtonVariant.tertiary ? 14 : 16,
- fontWeight:
- variant == ButtonVariant.tertiary
- ? FontWeight.w500
- : FontWeight.w600,
+ child: Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: widget.disabled ? null : widget.onPressed,
+ borderRadius: BorderRadius.circular(100),
+ splashFactory: NoSplash.splashFactory,
+ highlightColor: Colors.transparent,
+ child: Center(
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ if (widget.icon != null) ...[
+ widget.icon!,
+ const SizedBox(width: 10),
+ ],
+ Text(
+ widget.title,
+ style: GoogleFonts.nunito(
+ fontSize: widget.variant == ButtonVariant.tertiary
+ ? 14
+ : 16,
+ fontWeight: widget.variant == ButtonVariant.tertiary
+ ? FontWeight.w600
+ : FontWeight.w700,
+ color: _getTextColor(),
+ letterSpacing: 0.3,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
),
),
),
@@ -57,42 +102,92 @@ class PrimaryButton extends StatelessWidget {
}
Color _getBackgroundColor() {
- switch (variant) {
+ if (widget.disabled) {
+ return _getBaseBackgroundColor().withValues(alpha: 0.4);
+ }
+ if (_isPressed) {
+ return _getPressedBackgroundColor();
+ }
+ return _getBaseBackgroundColor();
+ }
+
+ Color _getBaseBackgroundColor() {
+ switch (widget.variant) {
case ButtonVariant.solid:
- return AppColors.primary;
+ return AppColors.coral;
case ButtonVariant.outline:
- return const Color(0xFF5A6B7F).withValues(alpha: 0.1);
+ return Colors.transparent;
case ButtonVariant.tertiary:
- return const Color(0xFF1A1F27);
+ return AppColors.backgroundSecondary;
}
}
- Color _getTextColor() {
- switch (variant) {
+ Color _getPressedBackgroundColor() {
+ switch (widget.variant) {
case ButtonVariant.solid:
- return const Color(0xFF0B0F14);
+ return AppColors.coralDark;
case ButtonVariant.outline:
- return const Color(0xFFB6C2D2);
+ return AppColors.coral.withValues(alpha: 0.1);
case ButtonVariant.tertiary:
- return const Color(0xFF8A96A6);
+ return AppColors.backgroundTertiary;
+ }
+ }
+
+ Color _getTextColor() {
+ if (widget.disabled) {
+ return _getBaseTextColor().withValues(alpha: 0.5);
}
+ return _getBaseTextColor();
}
- BorderSide _getBorderSide() {
- if (variant == ButtonVariant.outline) {
- return const BorderSide(color: Color(0xFF5A6B7F), width: 2);
+ Color _getBaseTextColor() {
+ switch (widget.variant) {
+ case ButtonVariant.solid:
+ return AppColors.background;
+ case ButtonVariant.outline:
+ return AppColors.textPrimary;
+ case ButtonVariant.tertiary:
+ return AppColors.textSecondary;
}
- return BorderSide.none;
}
- Color _getOverlayColor() {
- switch (variant) {
+ Border? _getBorder() {
+ switch (widget.variant) {
case ButtonVariant.solid:
- return AppColors.primary.withValues(alpha: 0.2);
+ return null;
case ButtonVariant.outline:
- return const Color(0xFF5A6B7F).withValues(alpha: 0.15);
+ return Border.all(
+ color: widget.disabled
+ ? AppColors.coral.withValues(alpha: 0.3)
+ : _isPressed
+ ? AppColors.coralLight
+ : AppColors.coral,
+ width: 2,
+ );
case ButtonVariant.tertiary:
- return const Color(0xFF2A3441).withValues(alpha: 0.3);
+ return Border.all(
+ color: AppColors.border,
+ width: 1,
+ );
}
}
+
+ List? _getBoxShadow() {
+ if (widget.disabled || widget.variant != ButtonVariant.solid) {
+ return null;
+ }
+
+ if (_isPressed) {
+ return null;
+ }
+
+ return [
+ // Subtle bottom shadow
+ BoxShadow(
+ color: AppColors.coralDark.withValues(alpha: 0.3),
+ blurRadius: 8,
+ offset: const Offset(0, 4),
+ ),
+ ];
+ }
}
diff --git a/linux/.gitignore b/linux/.gitignore
deleted file mode 100644
index d3896c9..0000000
--- a/linux/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-flutter/ephemeral
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
deleted file mode 100644
index 11c7550..0000000
--- a/linux/CMakeLists.txt
+++ /dev/null
@@ -1,128 +0,0 @@
-# Project-level configuration.
-cmake_minimum_required(VERSION 3.13)
-project(runner LANGUAGES CXX)
-
-# The name of the executable created for the application. Change this to change
-# the on-disk name of your application.
-set(BINARY_NAME "coves_flutter")
-# The unique GTK application identifier for this application. See:
-# https://wiki.gnome.org/HowDoI/ChooseApplicationID
-set(APPLICATION_ID "com.coves.coves_flutter")
-
-# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
-# versions of CMake.
-cmake_policy(SET CMP0063 NEW)
-
-# Load bundled libraries from the lib/ directory relative to the binary.
-set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
-
-# Root filesystem for cross-building.
-if(FLUTTER_TARGET_PLATFORM_SYSROOT)
- set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
- set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
- set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
- set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
- set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
- set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-endif()
-
-# Define build configuration options.
-if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
- set(CMAKE_BUILD_TYPE "Debug" CACHE
- STRING "Flutter build mode" FORCE)
- set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
- "Debug" "Profile" "Release")
-endif()
-
-# Compilation settings that should be applied to most targets.
-#
-# Be cautious about adding new options here, as plugins use this function by
-# default. In most cases, you should add new options to specific targets instead
-# of modifying this function.
-function(APPLY_STANDARD_SETTINGS TARGET)
- target_compile_features(${TARGET} PUBLIC cxx_std_14)
- target_compile_options(${TARGET} PRIVATE -Wall -Werror)
- target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
- target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
-endfunction()
-
-# Flutter library and tool build rules.
-set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
-add_subdirectory(${FLUTTER_MANAGED_DIR})
-
-# System-level dependencies.
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
-
-# Application build; see runner/CMakeLists.txt.
-add_subdirectory("runner")
-
-# Run the Flutter tool portions of the build. This must not be removed.
-add_dependencies(${BINARY_NAME} flutter_assemble)
-
-# Only the install-generated bundle's copy of the executable will launch
-# correctly, since the resources must in the right relative locations. To avoid
-# people trying to run the unbundled copy, put it in a subdirectory instead of
-# the default top-level location.
-set_target_properties(${BINARY_NAME}
- PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
-)
-
-
-# Generated plugin build rules, which manage building the plugins and adding
-# them to the application.
-include(flutter/generated_plugins.cmake)
-
-
-# === Installation ===
-# By default, "installing" just makes a relocatable bundle in the build
-# directory.
-set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
-if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
- set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
-endif()
-
-# Start with a clean build bundle directory every time.
-install(CODE "
- file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
- " COMPONENT Runtime)
-
-set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
-set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
-
-install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
- COMPONENT Runtime)
-
-install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
- COMPONENT Runtime)
-
-install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-
-foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
- install(FILES "${bundled_library}"
- DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-endforeach(bundled_library)
-
-# Copy the native assets provided by the build.dart from all packages.
-set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
-install(DIRECTORY "${NATIVE_ASSETS_DIR}"
- DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-
-# Fully re-copy the assets directory on each build to avoid having stale files
-# from a previous install.
-set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
-install(CODE "
- file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
- " COMPONENT Runtime)
-install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
- DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
-
-# Install the AOT library on non-Debug builds only.
-if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
- install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-endif()
diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt
deleted file mode 100644
index d5bd016..0000000
--- a/linux/flutter/CMakeLists.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-# This file controls Flutter-level build steps. It should not be edited.
-cmake_minimum_required(VERSION 3.10)
-
-set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
-
-# Configuration provided via flutter tool.
-include(${EPHEMERAL_DIR}/generated_config.cmake)
-
-# TODO: Move the rest of this into files in ephemeral. See
-# https://github.com/flutter/flutter/issues/57146.
-
-# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
-# which isn't available in 3.10.
-function(list_prepend LIST_NAME PREFIX)
- set(NEW_LIST "")
- foreach(element ${${LIST_NAME}})
- list(APPEND NEW_LIST "${PREFIX}${element}")
- endforeach(element)
- set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
-endfunction()
-
-# === Flutter Library ===
-# System-level dependencies.
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
-pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
-pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
-
-set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
-
-# Published to parent scope for install step.
-set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
-set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
-set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
-set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
-
-list(APPEND FLUTTER_LIBRARY_HEADERS
- "fl_basic_message_channel.h"
- "fl_binary_codec.h"
- "fl_binary_messenger.h"
- "fl_dart_project.h"
- "fl_engine.h"
- "fl_json_message_codec.h"
- "fl_json_method_codec.h"
- "fl_message_codec.h"
- "fl_method_call.h"
- "fl_method_channel.h"
- "fl_method_codec.h"
- "fl_method_response.h"
- "fl_plugin_registrar.h"
- "fl_plugin_registry.h"
- "fl_standard_message_codec.h"
- "fl_standard_method_codec.h"
- "fl_string_codec.h"
- "fl_value.h"
- "fl_view.h"
- "flutter_linux.h"
-)
-list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
-add_library(flutter INTERFACE)
-target_include_directories(flutter INTERFACE
- "${EPHEMERAL_DIR}"
-)
-target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
-target_link_libraries(flutter INTERFACE
- PkgConfig::GTK
- PkgConfig::GLIB
- PkgConfig::GIO
-)
-add_dependencies(flutter flutter_assemble)
-
-# === Flutter tool backend ===
-# _phony_ is a non-existent file to force this command to run every time,
-# since currently there's no way to get a full input/output list from the
-# flutter tool.
-add_custom_command(
- OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
- ${CMAKE_CURRENT_BINARY_DIR}/_phony_
- COMMAND ${CMAKE_COMMAND} -E env
- ${FLUTTER_TOOL_ENVIRONMENT}
- "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
- ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
- VERBATIM
-)
-add_custom_target(flutter_assemble DEPENDS
- "${FLUTTER_LIBRARY}"
- ${FLUTTER_LIBRARY_HEADERS}
-)
diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc
deleted file mode 100644
index 8be520b..0000000
--- a/linux/flutter/generated_plugin_registrant.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-// clang-format off
-
-#include "generated_plugin_registrant.h"
-
-#include
-#include
-#include
-#include
-
-void fl_register_plugins(FlPluginRegistry* registry) {
- g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
- desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
- g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
- flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
- g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
- url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
- g_autoptr(FlPluginRegistrar) window_to_front_registrar =
- fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin");
- window_to_front_plugin_register_with_registrar(window_to_front_registrar);
-}
diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h
deleted file mode 100644
index e0f0a47..0000000
--- a/linux/flutter/generated_plugin_registrant.h
+++ /dev/null
@@ -1,15 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-// clang-format off
-
-#ifndef GENERATED_PLUGIN_REGISTRANT_
-#define GENERATED_PLUGIN_REGISTRANT_
-
-#include
-
-// Registers Flutter plugins.
-void fl_register_plugins(FlPluginRegistry* registry);
-
-#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake
deleted file mode 100644
index 0245e78..0000000
--- a/linux/flutter/generated_plugins.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Generated file, do not edit.
-#
-
-list(APPEND FLUTTER_PLUGIN_LIST
- desktop_webview_window
- flutter_secure_storage_linux
- url_launcher_linux
- window_to_front
-)
-
-list(APPEND FLUTTER_FFI_PLUGIN_LIST
-)
-
-set(PLUGIN_BUNDLED_LIBRARIES)
-
-foreach(plugin ${FLUTTER_PLUGIN_LIST})
- add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
- target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
- list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
- list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
-endforeach(plugin)
-
-foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
- add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
- list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
-endforeach(ffi_plugin)
diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt
deleted file mode 100644
index e97dabc..0000000
--- a/linux/runner/CMakeLists.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-cmake_minimum_required(VERSION 3.13)
-project(runner LANGUAGES CXX)
-
-# Define the application target. To change its name, change BINARY_NAME in the
-# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
-# work.
-#
-# Any new source files that you add to the application should be added here.
-add_executable(${BINARY_NAME}
- "main.cc"
- "my_application.cc"
- "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
-)
-
-# Apply the standard set of build settings. This can be removed for applications
-# that need different build settings.
-apply_standard_settings(${BINARY_NAME})
-
-# Add preprocessor definitions for the application ID.
-add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
-
-# Add dependency libraries. Add any application-specific dependencies here.
-target_link_libraries(${BINARY_NAME} PRIVATE flutter)
-target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
-
-target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
diff --git a/linux/runner/main.cc b/linux/runner/main.cc
deleted file mode 100644
index e7c5c54..0000000
--- a/linux/runner/main.cc
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "my_application.h"
-
-int main(int argc, char** argv) {
- g_autoptr(MyApplication) app = my_application_new();
- return g_application_run(G_APPLICATION(app), argc, argv);
-}
diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc
deleted file mode 100644
index 2b63c3a..0000000
--- a/linux/runner/my_application.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-#include "my_application.h"
-
-#include
-#ifdef GDK_WINDOWING_X11
-#include
-#endif
-
-#include "flutter/generated_plugin_registrant.h"
-
-struct _MyApplication {
- GtkApplication parent_instance;
- char** dart_entrypoint_arguments;
-};
-
-G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
-
-// Implements GApplication::activate.
-static void my_application_activate(GApplication* application) {
- MyApplication* self = MY_APPLICATION(application);
- GtkWindow* window =
- GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
-
- // Use a header bar when running in GNOME as this is the common style used
- // by applications and is the setup most users will be using (e.g. Ubuntu
- // desktop).
- // If running on X and not using GNOME then just use a traditional title bar
- // in case the window manager does more exotic layout, e.g. tiling.
- // If running on Wayland assume the header bar will work (may need changing
- // if future cases occur).
- gboolean use_header_bar = TRUE;
-#ifdef GDK_WINDOWING_X11
- GdkScreen* screen = gtk_window_get_screen(window);
- if (GDK_IS_X11_SCREEN(screen)) {
- const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
- if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
- use_header_bar = FALSE;
- }
- }
-#endif
- if (use_header_bar) {
- GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
- gtk_widget_show(GTK_WIDGET(header_bar));
- gtk_header_bar_set_title(header_bar, "coves_flutter");
- gtk_header_bar_set_show_close_button(header_bar, TRUE);
- gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
- } else {
- gtk_window_set_title(window, "coves_flutter");
- }
-
- gtk_window_set_default_size(window, 1280, 720);
- gtk_widget_show(GTK_WIDGET(window));
-
- g_autoptr(FlDartProject) project = fl_dart_project_new();
- fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
-
- FlView* view = fl_view_new(project);
- gtk_widget_show(GTK_WIDGET(view));
- gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
-
- fl_register_plugins(FL_PLUGIN_REGISTRY(view));
-
- gtk_widget_grab_focus(GTK_WIDGET(view));
-}
-
-// Implements GApplication::local_command_line.
-static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
- MyApplication* self = MY_APPLICATION(application);
- // Strip out the first argument as it is the binary name.
- self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
-
- g_autoptr(GError) error = nullptr;
- if (!g_application_register(application, nullptr, &error)) {
- g_warning("Failed to register: %s", error->message);
- *exit_status = 1;
- return TRUE;
- }
-
- g_application_activate(application);
- *exit_status = 0;
-
- return TRUE;
-}
-
-// Implements GApplication::startup.
-static void my_application_startup(GApplication* application) {
- //MyApplication* self = MY_APPLICATION(object);
-
- // Perform any actions required at application startup.
-
- G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
-}
-
-// Implements GApplication::shutdown.
-static void my_application_shutdown(GApplication* application) {
- //MyApplication* self = MY_APPLICATION(object);
-
- // Perform any actions required at application shutdown.
-
- G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
-}
-
-// Implements GObject::dispose.
-static void my_application_dispose(GObject* object) {
- MyApplication* self = MY_APPLICATION(object);
- g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
- G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
-}
-
-static void my_application_class_init(MyApplicationClass* klass) {
- G_APPLICATION_CLASS(klass)->activate = my_application_activate;
- G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
- G_APPLICATION_CLASS(klass)->startup = my_application_startup;
- G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
- G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
-}
-
-static void my_application_init(MyApplication* self) {}
-
-MyApplication* my_application_new() {
- // Set the program name to the application ID, which helps various systems
- // like GTK and desktop environments map this running application to its
- // corresponding .desktop file. This ensures better integration by allowing
- // the application to be recognized beyond its binary name.
- g_set_prgname(APPLICATION_ID);
-
- return MY_APPLICATION(g_object_new(my_application_get_type(),
- "application-id", APPLICATION_ID,
- "flags", G_APPLICATION_NON_UNIQUE,
- nullptr));
-}
diff --git a/linux/runner/my_application.h b/linux/runner/my_application.h
deleted file mode 100644
index 72271d5..0000000
--- a/linux/runner/my_application.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef FLUTTER_MY_APPLICATION_H_
-#define FLUTTER_MY_APPLICATION_H_
-
-#include
-
-G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
- GtkApplication)
-
-/**
- * my_application_new:
- *
- * Creates a new Flutter-based application.
- *
- * Returns: a new #MyApplication.
- */
-MyApplication* my_application_new();
-
-#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/macos/.gitignore b/macos/.gitignore
deleted file mode 100644
index 746adbb..0000000
--- a/macos/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-# Flutter-related
-**/Flutter/ephemeral/
-**/Pods/
-
-# Xcode-related
-**/dgph
-**/xcuserdata/
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
deleted file mode 100644
index dffb322..0000000
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ /dev/null
@@ -1,30 +0,0 @@
-//
-// Generated file. Do not edit.
-//
-
-import FlutterMacOS
-import Foundation
-
-import desktop_webview_window
-import flutter_secure_storage_macos
-import flutter_web_auth_2
-import path_provider_foundation
-import share_plus
-import shared_preferences_foundation
-import sqflite_darwin
-import url_launcher_macos
-import video_player_avfoundation
-import window_to_front
-
-func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
- DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
- FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
- FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
- PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
- SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
- SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
- SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
- UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
- FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
- WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin"))
-}
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
deleted file mode 100644
index b3adaf2..0000000
--- a/macos/Runner.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,705 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 54;
- objects = {
-
-/* Begin PBXAggregateTarget section */
- 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
- isa = PBXAggregateTarget;
- buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
- buildPhases = (
- 33CC111E2044C6BF0003C045 /* ShellScript */,
- );
- dependencies = (
- );
- name = "Flutter Assemble";
- productName = FLX;
- };
-/* End PBXAggregateTarget section */
-
-/* Begin PBXBuildFile section */
- 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
- 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
- 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
- 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
- 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
- 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXContainerItemProxy section */
- 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 33CC10EC2044A3C60003C045;
- remoteInfo = Runner;
- };
- 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
- proxyType = 1;
- remoteGlobalIDString = 33CC111A2044C6BA0003C045;
- remoteInfo = FLX;
- };
-/* End PBXContainerItemProxy section */
-
-/* Begin PBXCopyFilesBuildPhase section */
- 33CC110E2044A8840003C045 /* Bundle Framework */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = "";
- dstSubfolderSpec = 10;
- files = (
- );
- name = "Bundle Framework";
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
- 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
- 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
- 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
- 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
- 33CC10ED2044A3C60003C045 /* coves_flutter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "coves_flutter.app"; sourceTree = BUILT_PRODUCTS_DIR; };
- 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
- 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
- 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
- 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
- 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
- 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
- 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
- 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
- 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
- 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
- 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
- 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
- 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- 331C80D2294CF70F00263BE5 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 33CC10EA2044A3C60003C045 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 331C80D6294CF71000263BE5 /* RunnerTests */ = {
- isa = PBXGroup;
- children = (
- 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
- );
- path = RunnerTests;
- sourceTree = "";
- };
- 33BA886A226E78AF003329D5 /* Configs */ = {
- isa = PBXGroup;
- children = (
- 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
- 9740EEB21CF90195004384FC /* Debug.xcconfig */,
- 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
- 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
- );
- path = Configs;
- sourceTree = "";
- };
- 33CC10E42044A3C60003C045 = {
- isa = PBXGroup;
- children = (
- 33FAB671232836740065AC1E /* Runner */,
- 33CEB47122A05771004F2AC0 /* Flutter */,
- 331C80D6294CF71000263BE5 /* RunnerTests */,
- 33CC10EE2044A3C60003C045 /* Products */,
- D73912EC22F37F3D000D13A0 /* Frameworks */,
- );
- sourceTree = "";
- };
- 33CC10EE2044A3C60003C045 /* Products */ = {
- isa = PBXGroup;
- children = (
- 33CC10ED2044A3C60003C045 /* coves_flutter.app */,
- 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
- );
- name = Products;
- sourceTree = "";
- };
- 33CC11242044D66E0003C045 /* Resources */ = {
- isa = PBXGroup;
- children = (
- 33CC10F22044A3C60003C045 /* Assets.xcassets */,
- 33CC10F42044A3C60003C045 /* MainMenu.xib */,
- 33CC10F72044A3C60003C045 /* Info.plist */,
- );
- name = Resources;
- path = ..;
- sourceTree = "";
- };
- 33CEB47122A05771004F2AC0 /* Flutter */ = {
- isa = PBXGroup;
- children = (
- 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
- 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
- 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
- 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
- );
- path = Flutter;
- sourceTree = "";
- };
- 33FAB671232836740065AC1E /* Runner */ = {
- isa = PBXGroup;
- children = (
- 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
- 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
- 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
- 33E51914231749380026EE4D /* Release.entitlements */,
- 33CC11242044D66E0003C045 /* Resources */,
- 33BA886A226E78AF003329D5 /* Configs */,
- );
- path = Runner;
- sourceTree = "";
- };
- D73912EC22F37F3D000D13A0 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- );
- name = Frameworks;
- sourceTree = "";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
- buildPhases = (
- 331C80D1294CF70F00263BE5 /* Sources */,
- 331C80D2294CF70F00263BE5 /* Frameworks */,
- 331C80D3294CF70F00263BE5 /* Resources */,
- );
- buildRules = (
- );
- dependencies = (
- 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
- );
- name = RunnerTests;
- productName = RunnerTests;
- productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
- productType = "com.apple.product-type.bundle.unit-test";
- };
- 33CC10EC2044A3C60003C045 /* Runner */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
- buildPhases = (
- 33CC10E92044A3C60003C045 /* Sources */,
- 33CC10EA2044A3C60003C045 /* Frameworks */,
- 33CC10EB2044A3C60003C045 /* Resources */,
- 33CC110E2044A8840003C045 /* Bundle Framework */,
- 3399D490228B24CF009A79C7 /* ShellScript */,
- );
- buildRules = (
- );
- dependencies = (
- 33CC11202044C79F0003C045 /* PBXTargetDependency */,
- );
- name = Runner;
- productName = Runner;
- productReference = 33CC10ED2044A3C60003C045 /* coves_flutter.app */;
- productType = "com.apple.product-type.application";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- 33CC10E52044A3C60003C045 /* Project object */ = {
- isa = PBXProject;
- attributes = {
- BuildIndependentTargetsInParallel = YES;
- LastSwiftUpdateCheck = 0920;
- LastUpgradeCheck = 1510;
- ORGANIZATIONNAME = "";
- TargetAttributes = {
- 331C80D4294CF70F00263BE5 = {
- CreatedOnToolsVersion = 14.0;
- TestTargetID = 33CC10EC2044A3C60003C045;
- };
- 33CC10EC2044A3C60003C045 = {
- CreatedOnToolsVersion = 9.2;
- LastSwiftMigration = 1100;
- ProvisioningStyle = Automatic;
- SystemCapabilities = {
- com.apple.Sandbox = {
- enabled = 1;
- };
- };
- };
- 33CC111A2044C6BA0003C045 = {
- CreatedOnToolsVersion = 9.2;
- ProvisioningStyle = Manual;
- };
- };
- };
- buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
- compatibilityVersion = "Xcode 9.3";
- developmentRegion = en;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- Base,
- );
- mainGroup = 33CC10E42044A3C60003C045;
- productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
- projectDirPath = "";
- projectRoot = "";
- targets = (
- 33CC10EC2044A3C60003C045 /* Runner */,
- 331C80D4294CF70F00263BE5 /* RunnerTests */,
- 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
- 331C80D3294CF70F00263BE5 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 33CC10EB2044A3C60003C045 /* Resources */ = {
- isa = PBXResourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
- 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXShellScriptBuildPhase section */
- 3399D490228B24CF009A79C7 /* ShellScript */ = {
- isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- );
- outputFileListPaths = (
- );
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
- };
- 33CC111E2044C6BF0003C045 /* ShellScript */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- Flutter/ephemeral/FlutterInputs.xcfilelist,
- );
- inputPaths = (
- Flutter/ephemeral/tripwire,
- );
- outputFileListPaths = (
- Flutter/ephemeral/FlutterOutputs.xcfilelist,
- );
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
- };
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
- 331C80D1294CF70F00263BE5 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
- 33CC10E92044A3C60003C045 /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
- 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
- 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXTargetDependency section */
- 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = 33CC10EC2044A3C60003C045 /* Runner */;
- targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
- };
- 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
- isa = PBXTargetDependency;
- target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
- targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
- };
-/* End PBXTargetDependency section */
-
-/* Begin PBXVariantGroup section */
- 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
- isa = PBXVariantGroup;
- children = (
- 33CC10F52044A3C60003C045 /* Base */,
- );
- name = MainMenu.xib;
- path = Runner;
- sourceTree = "";
- };
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
- 331C80DB294CF71000263BE5 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- CURRENT_PROJECT_VERSION = 1;
- GENERATE_INFOPLIST_FILE = YES;
- MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.coves.covesFlutter.RunnerTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/coves_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/coves_flutter";
- };
- name = Debug;
- };
- 331C80DC294CF71000263BE5 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- CURRENT_PROJECT_VERSION = 1;
- GENERATE_INFOPLIST_FILE = YES;
- MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.coves.covesFlutter.RunnerTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/coves_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/coves_flutter";
- };
- name = Release;
- };
- 331C80DD294CF71000263BE5 /* Profile */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- BUNDLE_LOADER = "$(TEST_HOST)";
- CURRENT_PROJECT_VERSION = 1;
- GENERATE_INFOPLIST_FILE = YES;
- MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.coves.covesFlutter.RunnerTests;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/coves_flutter.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/coves_flutter";
- };
- name = Profile;
- };
- 338D0CE9231458BD00FA5F75 /* Profile */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CODE_SIGN_IDENTITY = "-";
- COPY_PHASE_STRIP = NO;
- DEAD_CODE_STRIPPING = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- ENABLE_NS_ASSERTIONS = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_USER_SCRIPT_SANDBOXING = NO;
- GCC_C_LANGUAGE_STANDARD = gnu11;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.14;
- MTL_ENABLE_DEBUG_INFO = NO;
- SDKROOT = macosx;
- SWIFT_COMPILATION_MODE = wholemodule;
- SWIFT_OPTIMIZATION_LEVEL = "-O";
- };
- name = Profile;
- };
- 338D0CEA231458BD00FA5F75 /* Profile */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
- CODE_SIGN_STYLE = Automatic;
- COMBINE_HIDPI_IMAGES = YES;
- INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/../Frameworks",
- );
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_VERSION = 5.0;
- };
- name = Profile;
- };
- 338D0CEB231458BD00FA5F75 /* Profile */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CODE_SIGN_STYLE = Manual;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Profile;
- };
- 33CC10F92044A3C60003C045 /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CODE_SIGN_IDENTITY = "-";
- COPY_PHASE_STRIP = NO;
- DEAD_CODE_STRIPPING = YES;
- DEBUG_INFORMATION_FORMAT = dwarf;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_TESTABILITY = YES;
- ENABLE_USER_SCRIPT_SANDBOXING = NO;
- GCC_C_LANGUAGE_STANDARD = gnu11;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.14;
- MTL_ENABLE_DEBUG_INFO = YES;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = macosx;
- SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- };
- name = Debug;
- };
- 33CC10FA2044A3C60003C045 /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
- CLANG_ANALYZER_NONNULL = YES;
- CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
- CLANG_CXX_LIBRARY = "libc++";
- CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
- CLANG_WARN_BOOL_CONVERSION = YES;
- CLANG_WARN_CONSTANT_CONVERSION = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
- CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
- CLANG_WARN_EMPTY_BODY = YES;
- CLANG_WARN_ENUM_CONVERSION = YES;
- CLANG_WARN_INFINITE_RECURSION = YES;
- CLANG_WARN_INT_CONVERSION = YES;
- CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
- CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
- CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
- CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
- CLANG_WARN_SUSPICIOUS_MOVE = YES;
- CODE_SIGN_IDENTITY = "-";
- COPY_PHASE_STRIP = NO;
- DEAD_CODE_STRIPPING = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- ENABLE_NS_ASSERTIONS = NO;
- ENABLE_STRICT_OBJC_MSGSEND = YES;
- ENABLE_USER_SCRIPT_SANDBOXING = NO;
- GCC_C_LANGUAGE_STANDARD = gnu11;
- GCC_NO_COMMON_BLOCKS = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
- GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.14;
- MTL_ENABLE_DEBUG_INFO = NO;
- SDKROOT = macosx;
- SWIFT_COMPILATION_MODE = wholemodule;
- SWIFT_OPTIMIZATION_LEVEL = "-O";
- };
- name = Release;
- };
- 33CC10FC2044A3C60003C045 /* Debug */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
- CODE_SIGN_STYLE = Automatic;
- COMBINE_HIDPI_IMAGES = YES;
- INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/../Frameworks",
- );
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 5.0;
- };
- name = Debug;
- };
- 33CC10FD2044A3C60003C045 /* Release */ = {
- isa = XCBuildConfiguration;
- baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
- buildSettings = {
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
- CODE_SIGN_STYLE = Automatic;
- COMBINE_HIDPI_IMAGES = YES;
- INFOPLIST_FILE = Runner/Info.plist;
- LD_RUNPATH_SEARCH_PATHS = (
- "$(inherited)",
- "@executable_path/../Frameworks",
- );
- PROVISIONING_PROFILE_SPECIFIER = "";
- SWIFT_VERSION = 5.0;
- };
- name = Release;
- };
- 33CC111C2044C6BA0003C045 /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CODE_SIGN_STYLE = Manual;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Debug;
- };
- 33CC111D2044C6BA0003C045 /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CODE_SIGN_STYLE = Automatic;
- PRODUCT_NAME = "$(TARGET_NAME)";
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 331C80DB294CF71000263BE5 /* Debug */,
- 331C80DC294CF71000263BE5 /* Release */,
- 331C80DD294CF71000263BE5 /* Profile */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 33CC10F92044A3C60003C045 /* Debug */,
- 33CC10FA2044A3C60003C045 /* Release */,
- 338D0CE9231458BD00FA5F75 /* Profile */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 33CC10FC2044A3C60003C045 /* Debug */,
- 33CC10FD2044A3C60003C045 /* Release */,
- 338D0CEA231458BD00FA5F75 /* Profile */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 33CC111C2044C6BA0003C045 /* Debug */,
- 33CC111D2044C6BA0003C045 /* Release */,
- 338D0CEB231458BD00FA5F75 /* Profile */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = 33CC10E52044A3C60003C045 /* Project object */;
-}
diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
deleted file mode 100644
index 86644a0..0000000
--- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 1d526a1..0000000
--- a/macos/Runner.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift
deleted file mode 100644
index b3c1761..0000000
--- a/macos/Runner/AppDelegate.swift
+++ /dev/null
@@ -1,13 +0,0 @@
-import Cocoa
-import FlutterMacOS
-
-@main
-class AppDelegate: FlutterAppDelegate {
- override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
- return true
- }
-
- override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
- return true
- }
-}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index a2ec33f..0000000
--- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
- "images" : [
- {
- "size" : "16x16",
- "idiom" : "mac",
- "filename" : "app_icon_16.png",
- "scale" : "1x"
- },
- {
- "size" : "16x16",
- "idiom" : "mac",
- "filename" : "app_icon_32.png",
- "scale" : "2x"
- },
- {
- "size" : "32x32",
- "idiom" : "mac",
- "filename" : "app_icon_32.png",
- "scale" : "1x"
- },
- {
- "size" : "32x32",
- "idiom" : "mac",
- "filename" : "app_icon_64.png",
- "scale" : "2x"
- },
- {
- "size" : "128x128",
- "idiom" : "mac",
- "filename" : "app_icon_128.png",
- "scale" : "1x"
- },
- {
- "size" : "128x128",
- "idiom" : "mac",
- "filename" : "app_icon_256.png",
- "scale" : "2x"
- },
- {
- "size" : "256x256",
- "idiom" : "mac",
- "filename" : "app_icon_256.png",
- "scale" : "1x"
- },
- {
- "size" : "256x256",
- "idiom" : "mac",
- "filename" : "app_icon_512.png",
- "scale" : "2x"
- },
- {
- "size" : "512x512",
- "idiom" : "mac",
- "filename" : "app_icon_512.png",
- "scale" : "1x"
- },
- {
- "size" : "512x512",
- "idiom" : "mac",
- "filename" : "app_icon_1024.png",
- "scale" : "2x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
deleted file mode 100644
index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 102994
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