add dashboard to phone app
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../providers/dashboard_providers.dart';
|
||||
import '../../../widgets/error_view.dart';
|
||||
|
||||
class SettingsScreen extends ConsumerWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final async = ref.watch(brandSettingsProvider);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Brand Settings')),
|
||||
body: async.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => ErrorView(
|
||||
message: 'Failed to load settings',
|
||||
onRetry: () => ref.invalidate(brandSettingsProvider),
|
||||
),
|
||||
data: (settings) => _SettingsForm(settings: settings),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SettingsForm extends ConsumerStatefulWidget {
|
||||
final dynamic settings;
|
||||
|
||||
const _SettingsForm({required this.settings});
|
||||
|
||||
@override
|
||||
ConsumerState<_SettingsForm> createState() => _SettingsFormState();
|
||||
}
|
||||
|
||||
class _SettingsFormState extends ConsumerState<_SettingsForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _loading = false;
|
||||
|
||||
late final _displayName =
|
||||
TextEditingController(text: widget.settings.displayName as String);
|
||||
late final _tagline =
|
||||
TextEditingController(text: widget.settings.tagline as String?);
|
||||
late final _email =
|
||||
TextEditingController(text: widget.settings.publicEmail as String?);
|
||||
late final _phone =
|
||||
TextEditingController(text: widget.settings.publicPhone as String?);
|
||||
late final _city =
|
||||
TextEditingController(text: widget.settings.publicCity as String?);
|
||||
late final _country =
|
||||
TextEditingController(text: widget.settings.publicCountry as String?);
|
||||
late final _website =
|
||||
TextEditingController(text: widget.settings.websiteUrl as String?);
|
||||
late final _whatsapp =
|
||||
TextEditingController(text: widget.settings.whatsappNumber as String?);
|
||||
late bool _listed = widget.settings.isListedOnMarketplace as bool;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final c in [
|
||||
_displayName, _tagline, _email, _phone, _city, _country, _website, _whatsapp
|
||||
]) {
|
||||
c.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _submit() async {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
await ref.read(settingsServiceProvider).updateBrand({
|
||||
'displayName': _displayName.text.trim(),
|
||||
if (_tagline.text.isNotEmpty) 'tagline': _tagline.text.trim(),
|
||||
if (_email.text.isNotEmpty) 'publicEmail': _email.text.trim(),
|
||||
if (_phone.text.isNotEmpty) 'publicPhone': _phone.text.trim(),
|
||||
if (_city.text.isNotEmpty) 'publicCity': _city.text.trim(),
|
||||
if (_country.text.isNotEmpty) 'publicCountry': _country.text.trim(),
|
||||
if (_website.text.isNotEmpty) 'websiteUrl': _website.text.trim(),
|
||||
if (_whatsapp.text.isNotEmpty) 'whatsappNumber': _whatsapp.text.trim(),
|
||||
'isListedOnMarketplace': _listed,
|
||||
});
|
||||
ref.invalidate(brandSettingsProvider);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Settings saved')));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('Error: $e')));
|
||||
}
|
||||
} finally {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_section('Brand', [
|
||||
_field('Display Name', _displayName, required: true),
|
||||
_field('Tagline', _tagline),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_section('Contact', [
|
||||
_field('Public Email', _email,
|
||||
inputType: TextInputType.emailAddress),
|
||||
_field('Public Phone', _phone, inputType: TextInputType.phone),
|
||||
_field('WhatsApp Number', _whatsapp,
|
||||
inputType: TextInputType.phone),
|
||||
_field('Website URL', _website, inputType: TextInputType.url),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_section('Location', [
|
||||
_field('City', _city),
|
||||
_field('Country', _country),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
Card(
|
||||
child: SwitchListTile(
|
||||
title: const Text('Listed on Marketplace'),
|
||||
subtitle: const Text(
|
||||
'Show your fleet on the public marketplace',
|
||||
),
|
||||
value: _listed,
|
||||
onChanged: (v) => setState(() => _listed = v),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton(
|
||||
onPressed: _loading ? null : _submit,
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(48),
|
||||
),
|
||||
child: _loading
|
||||
? const SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: const Text('Save Settings'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _section(String title, List<Widget> children) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF6B7280))),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Column(children: children),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _field(String label, TextEditingController ctrl,
|
||||
{bool required = false, TextInputType? inputType}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: TextFormField(
|
||||
controller: ctrl,
|
||||
keyboardType: inputType,
|
||||
decoration: InputDecoration(labelText: label),
|
||||
validator: required
|
||||
? (v) => v == null || v.isEmpty ? 'Required' : null
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user