Implement core screens for authentication, home, automation, pricing, and social connection features.
This commit is contained in:
parent
7ba974fc38
commit
b3f9e30d4f
BIN
analysis_errors.txt
Normal file
BIN
analysis_errors.txt
Normal file
Binary file not shown.
BIN
analysis_full.txt
Normal file
BIN
analysis_full.txt
Normal file
Binary file not shown.
BIN
analysis_report.txt
Normal file
BIN
analysis_report.txt
Normal file
Binary file not shown.
187
lib/features/auth/screens/forgot_password_screen.dart
Normal file
187
lib/features/auth/screens/forgot_password_screen.dart
Normal file
@ -0,0 +1,187 @@
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:socialbuddy_mobile/core/constants/colors.dart';
|
||||
|
||||
class ForgotPasswordScreen extends StatefulWidget {
|
||||
const ForgotPasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ForgotPasswordScreen> createState() => _ForgotPasswordScreenState();
|
||||
}
|
||||
|
||||
class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
|
||||
final _emailController = TextEditingController();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_emailController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _handleResetPassword() async {
|
||||
if (_emailController.text.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Please enter your email")),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
// Mimic API call logic as in web
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
|
||||
if (mounted) {
|
||||
setState(() => _isLoading = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Password reset link sent to your email!")),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF0A0B17),
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
// Background Glows
|
||||
Positioned(
|
||||
top: 100,
|
||||
right: 50,
|
||||
child: _buildGlowBlob(const Color(0xFF1D8BE0), 120),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 100,
|
||||
left: 50,
|
||||
child: _buildGlowBlob(const Color(0xFFDB21D9), 120),
|
||||
),
|
||||
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.2)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.lock_reset, size: 80, color: Colors.blueAccent),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
"Forgot Password?",
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
"No worries! Enter your email and we'll send you a link to reset your password.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white.withOpacity(0.6),
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
TextField(
|
||||
controller: _emailController,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: _inputDecoration("Email Address", Icons.email_outlined),
|
||||
),
|
||||
|
||||
const SizedBox(height: 32),
|
||||
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF2563EB), Color(0xFFEC4899)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: _isLoading ? null : _handleResetPassword,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
child: _isLoading
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: const Text("SEND RESET LINK", style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGlowBlob(Color color, double size) {
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.3),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(color: color.withOpacity(0.5), blurRadius: 100, spreadRadius: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
InputDecoration _inputDecoration(String label, IconData icon) {
|
||||
return InputDecoration(
|
||||
hintText: label,
|
||||
hintStyle: TextStyle(color: Colors.white.withOpacity(0.5)),
|
||||
prefixIcon: Icon(icon, color: Colors.white70),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFF070D1E).withOpacity(0.7),
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.white.withOpacity(0.15)),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.white.withOpacity(0.15)),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(color: Colors.white.withOpacity(0.3)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
||||
import '../providers/auth_provider.dart';
|
||||
import '../../home/screens/home_screen.dart';
|
||||
import 'signup_screen.dart';
|
||||
import 'forgot_password_screen.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@ -139,7 +140,12 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {},
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const ForgotPasswordScreen()),
|
||||
);
|
||||
},
|
||||
child: const Text("Forgot Password?", style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
),
|
||||
|
||||
@ -5,6 +5,9 @@ import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../core/constants/api_constants.dart';
|
||||
import '../../connect/screens/social_connect_details_screen.dart';
|
||||
import '../../connect/screens/channels_screen.dart';
|
||||
import '../../posts/screens/posts_screen.dart';
|
||||
|
||||
class AutomationScreen extends StatefulWidget {
|
||||
const AutomationScreen({super.key});
|
||||
|
||||
@ -5,6 +5,8 @@ import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../core/constants/api_constants.dart';
|
||||
import '../../posts/screens/posts_screen.dart';
|
||||
import '../../automation/screens/automation_screen.dart';
|
||||
|
||||
class ChannelsScreen extends StatefulWidget {
|
||||
const ChannelsScreen({super.key});
|
||||
@ -37,16 +39,18 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
_channels = jsonDecode(response.body);
|
||||
setState(() {
|
||||
_channels = jsonDecode(response.body);
|
||||
});
|
||||
}
|
||||
|
||||
if (_connectedChannelId != null) {
|
||||
await _loadAccountDetails();
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error loading channels: $e");
|
||||
debugPrint("Error loading channels: $e");
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,10 +62,12 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
Uri.parse('${ApiConstants.liveBaseUrl}/social/account?userId=$email'),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
_accountData = jsonDecode(response.body);
|
||||
setState(() {
|
||||
_accountData = jsonDecode(response.body);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error loading account details: $e");
|
||||
debugPrint("Error loading account details: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,20 +85,31 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
final savedId = data['data']['channel_id'] ?? channelId;
|
||||
final savedId = data['data']?['channel_id'] ?? channelId;
|
||||
await prefs.setString('connectedChannel', savedId);
|
||||
setState(() => _connectedChannelId = savedId);
|
||||
setState(() => _connectedChannelId = savedId.toString());
|
||||
await _loadAccountDetails();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("$channelName connected successfully!")),
|
||||
);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("$channelName connected successfully!")),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
final error = jsonDecode(response.body);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(error['error'] ?? "Failed to connect $channelName")),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Failed to connect $channelName")),
|
||||
);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Error: $e")),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
if (mounted) setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,17 +120,28 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
_connectedChannelId = null;
|
||||
_accountData = null;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Channel removed successfully!")),
|
||||
);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Channel removed successfully!")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.darkBg,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
title: Text("Instagram Channels", style: GoogleFonts.nunito(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: _isLoading
|
||||
? const Center(child: CircularProgressIndicator(color: AppColors.primary))
|
||||
? const Center(child: CircularProgressIndicator(color: Colors.pinkAccent))
|
||||
: _connectedChannelId != null
|
||||
? _buildAccountDetails()
|
||||
: _buildChannelSelection(),
|
||||
@ -122,7 +150,7 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
|
||||
Widget _buildAccountDetails() {
|
||||
if (_accountData == null) {
|
||||
return const Center(child: CircularProgressIndicator(color: AppColors.primary));
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.pinkAccent));
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
@ -130,59 +158,180 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(32),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.cardBg,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
AppColors.cardBg.withOpacity(0.9),
|
||||
AppColors.cardBg.withOpacity(0.7),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.1)),
|
||||
boxShadow: [
|
||||
BoxShadow(color: const Color(0xFF833AB4).withOpacity(0.15), blurRadius: 30, offset: const Offset(0, 10)),
|
||||
BoxShadow(color: Colors.black.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 10)),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 60,
|
||||
backgroundImage: NetworkImage(_accountData!['profile_picture_url'] ?? ''),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
_accountData!['username'] ?? 'User',
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_buildStatItem(_accountData!['media_count'].toString(), "Posts"),
|
||||
_buildStatItem(_accountData!['followers_count'].toString(), "Followers"),
|
||||
_buildStatItem(_accountData!['follows_count'].toString(), "Following"),
|
||||
Container(
|
||||
width: 130,
|
||||
height: 130,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF833AB4), Color(0xFFFD1D1D), Color(0xFFFCAF45)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFFFD1D1D).withOpacity(0.4),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 122,
|
||||
height: 122,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.cardBg, // Border effect
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: CircleAvatar(
|
||||
radius: 56,
|
||||
backgroundColor: Colors.black,
|
||||
backgroundImage: NetworkImage(_accountData!['profile_picture_url'] ?? ''),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 10,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFD1D1D),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: AppColors.cardBg, width: 3),
|
||||
),
|
||||
child: const Icon(Icons.check, color: Colors.white, size: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (_accountData!['biography'] != null)
|
||||
Text(
|
||||
_accountData!['biography'],
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(color: Colors.white70),
|
||||
Text(
|
||||
_accountData!['username'] ?? 'User',
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
if (_accountData!['name'] != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
_accountData!['name'],
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 16,
|
||||
color: Colors.white54,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.03),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.05)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_buildStatItem(_accountData!['media_count']?.toString() ?? "0", "Posts"),
|
||||
Container(width: 1, height: 40, color: Colors.white10),
|
||||
_buildStatItem(_accountData!['followers_count']?.toString() ?? "0", "Followers"),
|
||||
Container(width: 1, height: 40, color: Colors.white10),
|
||||
_buildStatItem(_accountData!['follows_count']?.toString() ?? "0", "Following"),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
if (_accountData!['biography'] != null)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Text(
|
||||
_accountData!['biography'],
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(color: Colors.white70, fontSize: 14, height: 1.5),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
child: ElevatedButton(
|
||||
onPressed: _removeChannel,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red.shade700,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
backgroundColor: Colors.red.withOpacity(0.1),
|
||||
side: BorderSide(color: Colors.red.withOpacity(0.5), width: 1),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
elevation: 0,
|
||||
).copyWith(
|
||||
overlayColor: MaterialStateProperty.all(Colors.red.withOpacity(0.1)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.logout, color: Colors.red),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
"Disconnect Channel",
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.red,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Text("Remove Channel"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.info_outline, color: Colors.white38, size: 16),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"Switch channels by disconnecting current one",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(color: Colors.white38, fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -191,47 +340,236 @@ class _ChannelsScreenState extends State<ChannelsScreen> {
|
||||
Widget _buildStatItem(String value, String label) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(value, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white)),
|
||||
Text(label, style: const TextStyle(fontSize: 12, color: Colors.white54)),
|
||||
Text(
|
||||
value,
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 12,
|
||||
color: Colors.white54,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.5,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildChannelSelection() {
|
||||
if (_channels.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.purple.withOpacity(0.2), Colors.pink.withOpacity(0.2)],
|
||||
),
|
||||
),
|
||||
child: const Icon(Icons.account_circle_outlined, size: 64, color: Colors.white24),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"No Instagram Channels",
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Text(
|
||||
"Connect via Facebook to get started",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white54,
|
||||
fontSize: 13,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
itemCount: _channels.length,
|
||||
itemBuilder: (context, index) {
|
||||
final ch = _channels[index];
|
||||
final hasLinked = ch['has_linked_account'] == true;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.cardBg,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.1)),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
ch['name'],
|
||||
style: GoogleFonts.nunito(fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: hasLinked ? () => _connectChannel(ch['id'], ch['name']) : null,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: hasLinked ? AppColors.primary : Colors.grey.shade800,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
child: Text(hasLinked ? "Connect" : "Not Linked"),
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
gradient: LinearGradient(
|
||||
colors: hasLinked
|
||||
? [Colors.purple.withOpacity(0.1), Colors.pink.withOpacity(0.1)]
|
||||
: [Colors.grey.withOpacity(0.05), Colors.grey.withOpacity(0.05)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: hasLinked
|
||||
? Colors.purpleAccent.withOpacity(0.3)
|
||||
: Colors.white.withOpacity(0.1),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: hasLinked
|
||||
? Colors.purpleAccent.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.2),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
onTap: hasLinked ? () => _connectChannel(ch['id'].toString(), ch['name']) : null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
// Instagram Icon
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
gradient: hasLinked
|
||||
? const LinearGradient(
|
||||
colors: [Color(0xFF833AB4), Color(0xFFFD1D1D), Color(0xFFFCAF45)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: LinearGradient(
|
||||
colors: [Colors.grey.shade800, Colors.grey.shade700],
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: hasLinked
|
||||
? Colors.purpleAccent.withOpacity(0.2)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.camera_alt_rounded,
|
||||
color: Colors.white,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Channel Info
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
ch['name'] ?? 'Channel',
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (hasLinked) ...[
|
||||
const SizedBox(width: 6),
|
||||
Icon(Icons.check_circle, size: 14, color: Colors.greenAccent),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
hasLinked ? "Ready" : "Auth Required",
|
||||
style: GoogleFonts.nunito(
|
||||
fontSize: 11,
|
||||
color: hasLinked ? Colors.greenAccent : Colors.orangeAccent,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Connect Button
|
||||
Container(
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
gradient: hasLinked
|
||||
? const LinearGradient(
|
||||
colors: [Color(0xFF667EEA), Color(0xFF764BA2)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
)
|
||||
: null,
|
||||
color: hasLinked ? null : Colors.white.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
onTap: hasLinked ? () => _connectChannel(ch['id'].toString(), ch['name']) : null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
hasLinked ? "Link" : "Lock",
|
||||
style: GoogleFonts.nunito(
|
||||
color: hasLinked ? Colors.white : Colors.white38,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
hasLinked ? Icons.arrow_forward_rounded : Icons.lock_outline,
|
||||
color: hasLinked ? Colors.white : Colors.white38,
|
||||
size: 14,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@ -5,6 +5,9 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../services/social_auth_service.dart';
|
||||
import 'channels_screen.dart';
|
||||
import '../../posts/screens/posts_screen.dart';
|
||||
import '../../automation/screens/automation_screen.dart';
|
||||
|
||||
class SocialMediaConnectScreen extends StatefulWidget {
|
||||
final bool isTab;
|
||||
@ -224,7 +227,7 @@ class _SocialMediaConnectScreenState extends State<SocialMediaConnectScreen> wit
|
||||
// Disconnect Button
|
||||
if (_status == "connected")
|
||||
_buildFullWidthButton(
|
||||
text: "Remove Facebook Account",
|
||||
text: "Disconnect Facebook",
|
||||
onPressed: _isLoading ? null : _handleDisconnect,
|
||||
color: const Color(0xFFE11D48),
|
||||
),
|
||||
@ -264,16 +267,16 @@ class _SocialMediaConnectScreenState extends State<SocialMediaConnectScreen> wit
|
||||
Widget _buildGradientButton({required String text, required VoidCallback? onPressed, required List<Color> colors}) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 60,
|
||||
height: 56,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: colors),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: colors.first.withOpacity(0.3),
|
||||
blurRadius: 15,
|
||||
offset: const Offset(0, 5),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -281,12 +284,16 @@ class _SocialMediaConnectScreenState extends State<SocialMediaConnectScreen> wit
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.nunito(fontSize: 18, fontWeight: FontWeight.w800, color: Colors.white),
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -296,20 +303,26 @@ class _SocialMediaConnectScreenState extends State<SocialMediaConnectScreen> wit
|
||||
Widget _buildFullWidthButton({required String text, required VoidCallback? onPressed, required Color color}) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 60,
|
||||
height: 56,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: color,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)),
|
||||
elevation: 4,
|
||||
shadowColor: color.withOpacity(0.4),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
shadowColor: Colors.transparent,
|
||||
).copyWith(
|
||||
overlayColor: MaterialStateProperty.all(Colors.white.withOpacity(0.1)),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: _isLoading
|
||||
? const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2))
|
||||
: Text(
|
||||
text,
|
||||
style: GoogleFonts.nunito(fontSize: 18, fontWeight: FontWeight.w800, color: Colors.white),
|
||||
: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
child: Text(
|
||||
text,
|
||||
style: GoogleFonts.nunito(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:socialbuddy_mobile/features/automation/screens/automation_screen
|
||||
import 'package:socialbuddy_mobile/features/connect/screens/channels_screen.dart';
|
||||
|
||||
import 'package:socialbuddy_mobile/features/profile/screens/profile_screen.dart';
|
||||
import 'package:socialbuddy_mobile/features/profile/screens/pricing_screen.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
@ -23,8 +24,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
|
||||
static final List<Widget> _widgetOptions = <Widget>[
|
||||
const DashboardTab(),
|
||||
const PostsScreen(),
|
||||
const SocialMediaConnectScreen(),
|
||||
const ChannelsScreen(),
|
||||
const PostsScreen(),
|
||||
const AutomationScreen(),
|
||||
const ProfileScreen(),
|
||||
];
|
||||
|
||||
@ -46,17 +49,6 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
child: Image.asset('assets/images/logo_sb.png', errorBuilder: (c, e, s) => const Icon(Icons.hub, color: Colors.blue)),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.05),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.grid_view_rounded, color: Colors.white70, size: 20),
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
IconButton(
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
@ -66,7 +58,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
),
|
||||
child: const Icon(Icons.person_outline, color: Colors.white70, size: 20),
|
||||
),
|
||||
onPressed: () => _onItemTapped(3),
|
||||
onPressed: () => _onItemTapped(5), // Navigate to Profile (index 5)
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
@ -83,14 +75,25 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
activeIcon: Icon(Icons.grid_view),
|
||||
label: 'Home',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.link),
|
||||
activeIcon: Icon(Icons.link),
|
||||
label: 'Connect',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.account_circle_outlined),
|
||||
activeIcon: Icon(Icons.account_circle),
|
||||
label: 'Channels',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.post_add_outlined),
|
||||
activeIcon: Icon(Icons.post_add),
|
||||
label: 'Posts',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.link),
|
||||
label: 'Connect',
|
||||
icon: Icon(Icons.comment_outlined),
|
||||
activeIcon: Icon(Icons.comment),
|
||||
label: 'Automation',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.person_outline),
|
||||
@ -127,10 +130,10 @@ class DashboardTab extends StatelessWidget {
|
||||
if (role == 'customer') {
|
||||
final session = prefs.getString('payment_session');
|
||||
if (session == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Pricing check required. Please subscribe.")),
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const PricingScreen()),
|
||||
);
|
||||
// In a real app, you would navigate to PricingScreen
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -205,14 +208,21 @@ class DashboardTab extends StatelessWidget {
|
||||
description: "Automatically reply to comments/messages using smart automation.",
|
||||
icon: Icons.chat_bubble_outline_rounded,
|
||||
iconColor: Colors.blueAccent,
|
||||
onTap: () {}, // Tab removed in 4-tab layout
|
||||
onTap: () => _checkAndNavigate(context, const AutomationScreen()),
|
||||
),
|
||||
FeatureCard(
|
||||
title: "Instagram Channels",
|
||||
description: "Select and connect specific Instagram business accounts to manage.",
|
||||
icon: Icons.account_tree_outlined,
|
||||
iconColor: Colors.pinkAccent,
|
||||
onTap: () => _checkAndNavigate(context, const ChannelsScreen()),
|
||||
),
|
||||
FeatureCard(
|
||||
title: "Instagram Media",
|
||||
description: "View all Instagram media from your connected accounts with insights.",
|
||||
icon: Icons.camera_alt_outlined,
|
||||
iconColor: Colors.pinkAccent,
|
||||
onTap: () => homeState?._onItemTapped(2),
|
||||
iconColor: Colors.cyanAccent,
|
||||
onTap: () => homeState?._onItemTapped(1),
|
||||
),
|
||||
FeatureCard(
|
||||
title: "Social Media Posts",
|
||||
@ -226,21 +236,33 @@ class DashboardTab extends StatelessWidget {
|
||||
description: "Track auto reply logs and automation actions with timestamps.",
|
||||
icon: Icons.history_rounded,
|
||||
iconColor: Colors.orangeAccent,
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Automation Logs coming soon!")),
|
||||
);
|
||||
},
|
||||
),
|
||||
FeatureCard(
|
||||
title: "Users",
|
||||
description: "Manage users, roles, and access permissions.",
|
||||
icon: Icons.person_search_outlined,
|
||||
iconColor: Colors.purpleAccent,
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("User Management coming soon!")),
|
||||
);
|
||||
},
|
||||
),
|
||||
FeatureCard(
|
||||
title: "Reports",
|
||||
description: "Analyze insights, performance charts, and engagement metrics.",
|
||||
icon: Icons.auto_graph_rounded,
|
||||
iconColor: Colors.cyanAccent,
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Reports and Analytics coming soon!")),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Center(
|
||||
|
||||
@ -1,117 +1,357 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:socialbuddy_mobile/core/constants/colors.dart';
|
||||
import 'package:socialbuddy_mobile/core/constants/api_constants.dart';
|
||||
import '../../connect/screens/channels_screen.dart';
|
||||
import '../../connect/screens/social_connect_details_screen.dart';
|
||||
import '../../automation/screens/automation_screen.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
|
||||
class PostsScreen extends StatelessWidget {
|
||||
class PostsScreen extends StatefulWidget {
|
||||
const PostsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Dummy posts data
|
||||
final List<Map<String, String>> posts = List.generate(10, (index) => {
|
||||
'image': 'https://picsum.photos/500/500?random=$index',
|
||||
'caption': 'This is a sample post caption for Social Buddy #social #marketing',
|
||||
'likes': '${(index + 1) * 12}',
|
||||
'comments': '${index * 3}',
|
||||
'type': index % 3 == 0 ? 'VIDEO' : 'IMAGE',
|
||||
State<PostsScreen> createState() => _PostsScreenState();
|
||||
}
|
||||
|
||||
class _PostsScreenState extends State<PostsScreen> {
|
||||
List<dynamic> _media = [];
|
||||
bool _isLoading = true;
|
||||
String? _error;
|
||||
String? _connectedChannelName;
|
||||
Map<String, dynamic>? _accountData;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchMedia();
|
||||
}
|
||||
|
||||
Future<void> _fetchMedia() async {
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final userEmail = prefs.getString('user_email');
|
||||
final connectedChannelId = prefs.getString('connectedChannel');
|
||||
|
||||
if (userEmail == null) {
|
||||
setState(() {
|
||||
_error = "User email not found. Please log in again.";
|
||||
_isLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have account details saved or fetch them
|
||||
if (connectedChannelId != null) {
|
||||
await _fetchAccountDetails(userEmail);
|
||||
}
|
||||
|
||||
final url = Uri.parse('${ApiConstants.liveBaseUrl}/social/media?userId=$userEmail&limit=20');
|
||||
final response = await http.get(url);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
setState(() {
|
||||
_media = data['data'] ?? [];
|
||||
_isLoading = false;
|
||||
});
|
||||
} else {
|
||||
final data = jsonDecode(response.body);
|
||||
setState(() {
|
||||
// Backend might return 'error' or 'message'
|
||||
_error = data['error'] ?? data['message'] ?? "Failed to load media";
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_error = "An error occurred: $e";
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchAccountDetails(String email) async {
|
||||
try {
|
||||
final response = await http.get(
|
||||
Uri.parse('${ApiConstants.liveBaseUrl}/social/account?userId=$email'),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
setState(() {
|
||||
_accountData = data;
|
||||
_connectedChannelName = data['username'];
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error fetching account for posts header: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.darkBg,
|
||||
body: GridView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 0.65,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Media Library", style: GoogleFonts.nunito(fontWeight: FontWeight.bold)),
|
||||
if (_connectedChannelName != null)
|
||||
Text("Account: @$_connectedChannelName",
|
||||
style: GoogleFonts.nunito(fontSize: 12, color: Colors.white54)),
|
||||
],
|
||||
),
|
||||
itemCount: posts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final post = posts[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.cardBg,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.05)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
|
||||
child: Image.network(
|
||||
post['image']!,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
post['type']!,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 8),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
post['caption']!,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: GoogleFonts.nunito(fontSize: 12, color: Colors.white),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.favorite, size: 14, color: AppColors.danger),
|
||||
const SizedBox(width: 4),
|
||||
Text(post['likes']!, style: const TextStyle(fontSize: 10, color: Colors.white70)),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.comment, size: 14, color: Colors.blueAccent),
|
||||
const SizedBox(width: 4),
|
||||
Text(post['comments']!, style: const TextStyle(fontSize: 10, color: Colors.white70)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: _fetchMedia,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: _fetchMedia,
|
||||
color: Colors.pinkAccent,
|
||||
child: _buildBody(),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {},
|
||||
backgroundColor: AppColors.primary,
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text("Create Post feature coming soon!")),
|
||||
);
|
||||
},
|
||||
backgroundColor: Colors.blueAccent,
|
||||
child: const Icon(Icons.add, color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
if (_isLoading) {
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.pinkAccent));
|
||||
}
|
||||
|
||||
if (_error != null) {
|
||||
return SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.7,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error_outline, size: 64, color: Colors.orangeAccent),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_error!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
if (_error!.contains("Connect a channel"))
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const ChannelsScreen()),
|
||||
).then((_) => _fetchMedia()),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.pinkAccent),
|
||||
child: const Text("Go to Channels"),
|
||||
)
|
||||
else
|
||||
ElevatedButton(
|
||||
onPressed: _fetchMedia,
|
||||
style: ElevatedButton.styleFrom(backgroundColor: Colors.blueAccent),
|
||||
child: const Text("Retry"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (_media.isEmpty) {
|
||||
return SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.7,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.photo_library_outlined, size: 80, color: Colors.white10),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
"No Instagram media found.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
"Make sure you have an active Instagram business account connected.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white38),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const ChannelsScreen()),
|
||||
).then((_) => _fetchMedia()),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
child: const Text("Connect Channel"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return GridView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
childAspectRatio: 0.65,
|
||||
crossAxisSpacing: 16,
|
||||
mainAxisSpacing: 16,
|
||||
),
|
||||
itemCount: _media.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _media[index];
|
||||
final mediaType = item['media_type'];
|
||||
final mediaUrl = item['media_url'];
|
||||
final caption = item['caption'] ?? "";
|
||||
final likes = item['like_count']?.toString() ?? "0";
|
||||
final comments = item['comments_count']?.toString() ?? "0";
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.cardBg,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.white.withOpacity(0.05)),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.black26, blurRadius: 10, offset: const Offset(0, 5)),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
|
||||
child: Image.network(
|
||||
mediaUrl,
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
loadingBuilder: (context, child, loadingProgress) {
|
||||
if (loadingProgress == null) return child;
|
||||
return Container(
|
||||
color: Colors.white.withOpacity(0.02),
|
||||
child: const Center(child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
);
|
||||
},
|
||||
errorBuilder: (c, e, s) => Container(
|
||||
color: Colors.white10,
|
||||
child: const Center(child: Icon(Icons.broken_image, color: Colors.white24, size: 32)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 8,
|
||||
right: 8,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54.withOpacity(0.6),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
_getMediaTypeName(mediaType),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 8, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
caption,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: GoogleFonts.nunito(fontSize: 12, color: Colors.white, height: 1.3),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildStatChip(Icons.favorite, likes, Colors.pinkAccent),
|
||||
_buildStatChip(Icons.comment, comments, Colors.blueAccent),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _getMediaTypeName(String type) {
|
||||
switch (type) {
|
||||
case 'IMAGE': return 'PHOTO';
|
||||
case 'VIDEO': return 'VIDEO';
|
||||
case 'CAROUSEL_ALBUM': return 'CAROUSEL';
|
||||
default: return type;
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildStatChip(IconData icon, String value, Color color) {
|
||||
return Row(
|
||||
children: [
|
||||
Icon(icon, size: 14, color: color),
|
||||
const SizedBox(width: 4),
|
||||
Text(value, style: const TextStyle(fontSize: 10, color: Colors.white70, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
306
lib/features/profile/screens/pricing_screen.dart
Normal file
306
lib/features/profile/screens/pricing_screen.dart
Normal file
@ -0,0 +1,306 @@
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
class PricingScreen extends StatefulWidget {
|
||||
const PricingScreen({super.key});
|
||||
|
||||
@override
|
||||
State<PricingScreen> createState() => _PricingScreenState();
|
||||
}
|
||||
|
||||
class _PricingScreenState extends State<PricingScreen> {
|
||||
bool _isYearly = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF0A0B17),
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
title: Text("Pricing Plans",
|
||||
style: GoogleFonts.nunito(color: Colors.white, fontWeight: FontWeight.bold)),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: Colors.white),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
// Background Glows
|
||||
Positioned(top: 100, left: -50, child: _buildGlowBlob(const Color(0xFF1D8BE0), 150)),
|
||||
Positioned(bottom: 50, right: -50, child: _buildGlowBlob(const Color(0xFFEC4899), 150)),
|
||||
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Choose Your Plan",
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"Unlock premium features and grow your social presence.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white.withOpacity(0.6),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
// Billing Toggle
|
||||
Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildToggleButton("Monthly", !_isYearly),
|
||||
_buildToggleButton("Yearly (Save 20%)", _isYearly),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
// Free Trial
|
||||
_buildPricingCard(
|
||||
title: "7-Day Free Trial",
|
||||
price: "\$0",
|
||||
duration: "7 Days",
|
||||
desc: "Experience SocialBuddy with no commitment.",
|
||||
features: ["Full Platform Access", "All Premium Features", "No Credits Required", "Cancel Anytime"],
|
||||
isPopular: false,
|
||||
color: Colors.cyanAccent,
|
||||
buttonText: "START FREE TRIAL",
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Basic Buddy
|
||||
_buildPricingCard(
|
||||
title: "Basic Buddy",
|
||||
price: _isYearly ? "\$100" : "\$10",
|
||||
duration: _isYearly ? "Year" : "Month",
|
||||
desc: "Perfect for individual creators.",
|
||||
features: ["1 Social Profile", "Basic Analytics", "Content Scheduling", "Standard Support"],
|
||||
isPopular: false,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Standard Buddy
|
||||
_buildPricingCard(
|
||||
title: "Standard Buddy",
|
||||
price: _isYearly ? "\$500" : "\$50",
|
||||
duration: _isYearly ? "Year" : "Month",
|
||||
desc: "For growing businesses and influencers.",
|
||||
features: ["10 Social Profiles", "Advanced Analytics", "AI Content Generation", "Priority Support", "Team Collaboration (3)"],
|
||||
isPopular: true,
|
||||
color: const Color(0xFFEC4899),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Premium Buddy
|
||||
_buildPricingCard(
|
||||
title: "Premium Buddy",
|
||||
price: _isYearly ? "\$1500" : "\$150",
|
||||
duration: _isYearly ? "Year" : "Month",
|
||||
desc: "Ultimate solution for agencies.",
|
||||
features: ["25 Social Profiles", "White Label Reports", "Dedicated Account Manager", "API Access", "Unlimited Team Members"],
|
||||
isPopular: false,
|
||||
color: Colors.purpleAccent,
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggleButton(String label, bool isActive) {
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _isYearly = label.contains("Yearly")),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
gradient: isActive
|
||||
? const LinearGradient(colors: [Color(0xFF4361EE), Color(0xFFC026D3)])
|
||||
: null,
|
||||
color: isActive ? null : Colors.transparent,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.nunito(
|
||||
color: isActive ? Colors.white : Colors.white38,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPricingCard({
|
||||
required String title,
|
||||
required String price,
|
||||
required String duration,
|
||||
required String desc,
|
||||
required List<String> features,
|
||||
required bool isPopular,
|
||||
required Color color,
|
||||
String buttonText = "GET STARTED",
|
||||
}) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1A1D26).withOpacity(0.8),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
border: Border.all(
|
||||
color: isPopular ? color : Colors.white.withOpacity(0.1),
|
||||
width: isPopular ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
if (isPopular)
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topRight: Radius.circular(22),
|
||||
bottomLeft: Radius.circular(24),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
"POPULAR",
|
||||
style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(28),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
desc,
|
||||
style: GoogleFonts.nunito(color: Colors.white54, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
price,
|
||||
style: GoogleFonts.nunito(
|
||||
color: Colors.white,
|
||||
fontSize: 40,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8, left: 4),
|
||||
child: Text(
|
||||
"/$duration",
|
||||
style: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Divider(color: Colors.white10),
|
||||
const SizedBox(height: 24),
|
||||
...features.map((f) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(Icons.check, color: color, size: 16),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(f, style: const TextStyle(color: Colors.white70, fontSize: 14)),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
const SizedBox(height: 32),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [color, color.withOpacity(0.7)]),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(color: color.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4)),
|
||||
],
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Subscription to $title coming soon!")),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.transparent,
|
||||
shadowColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
),
|
||||
child: Text(buttonText,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16)),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGlowBlob(Color color, double size) {
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.2),
|
||||
shape: BoxShape.circle,
|
||||
boxShadow: [
|
||||
BoxShadow(color: color.withOpacity(0.4), blurRadius: 100, spreadRadius: 10),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user