You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
15 KiB

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supplier_new/common/settings.dart'; // for fontTextStyle
class AddTransactionScreen extends StatefulWidget {
const AddTransactionScreen({super.key});
@override
State<AddTransactionScreen> createState() => _AddTransactionScreenState();
}
class _AddTransactionScreenState extends State<AddTransactionScreen> {
final _formKey = GlobalKey<FormState>();
String? transactionType;
String? accountName;
String? paymentMode;
String? paymentStatus;
final TextEditingController amountController = TextEditingController();
final TextEditingController transactionIdController = TextEditingController();
final TextEditingController dateController = TextEditingController();
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.dark.copyWith(
statusBarColor: Colors.white,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
child: Theme(
data: Theme.of(context).copyWith(
scaffoldBackgroundColor: Colors.white,
canvasColor: Colors.white,
dialogBackgroundColor: Colors.white,
dropdownMenuTheme: const DropdownMenuThemeData(
menuStyle: MenuStyle(
backgroundColor: WidgetStatePropertyAll(Colors.white),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: Colors.white,
labelStyle:
fontTextStyle(12, const Color(0xFF6B7280), FontWeight.w800),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFFE5E7EB)),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFFE5E7EB)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide:
const BorderSide(color: Color(0xFF8270DB), width: 1.5),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
),
appBarTheme: const AppBarTheme(
backgroundColor: Colors.white,
elevation: 0,
surfaceTintColor: Colors.white,
foregroundColor: Color(0xFF2A2A2A),
systemOverlayStyle: SystemUiOverlayStyle.dark,
),
),
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
leading: IconButton(
onPressed: () => Navigator.pop(context),
tooltip: 'Back',
icon: Image.asset(
'images/backbutton_appbar.png',
width: 24,
height: 24,
color: const Color(0XFF2A2A2A),
),
),
title: Text(
"Add Transaction",
style:
fontTextStyle(16, const Color(0xFF2A2A2A), FontWeight.w600),
),
actions: [
TextButton(
onPressed: () {},
child: Text(
"HELP",
style: fontTextStyle(
12, const Color(0xFF8270DB), FontWeight.w600),
),
),
],
),
body: SafeArea(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel("Transaction Type", requiredMark: true),
buildDropdownHint(
hint: "Select type",
items: const ["Credit", "Debit"],
value: transactionType,
onChanged: (val) =>
setState(() => transactionType = val),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
const SizedBox(height: 16),
fieldLabel("Account Name", requiredMark: true),
buildDropdownHint(
hint: " name",
items: const ["Name1", "Name2"],
value: accountName,
onChanged: (val) => setState(() => accountName = val),
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
const SizedBox(height: 16),
fieldLabel("Amount (in ₹)", requiredMark: true),
buildTextField(
hint: "Enter amount",
controller: amountController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
validator: (v) =>
(v == null || v
.trim()
.isEmpty)
? "Amount is required"
: null,
hintStyle: fontTextStyle(
14, const Color(0xFF939495), FontWeight.w400),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel("Payment Status",
requiredMark: true),
buildDropdownHint(
hint: "Select status",
items: const [
"Pending",
"Completed",
"Failed"
],
value: paymentStatus,
onChanged: (val) =>
setState(() => paymentStatus = val),
hintStyle: fontTextStyle(
14,
const Color(0xFF939495),
FontWeight.w400), // ← styled
),
],
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
fieldLabel("Date", requiredMark: true),
buildDatePicker(hint: "DD-MM-YYYY"),
],
),
),
],
),
const SizedBox(height: 16),
fieldLabel("Transaction ID", requiredMark: true),
buildTextField(
hint: "Transaction ID *",
controller: transactionIdController,
validator: (v) =>
(v == null || v
.trim()
.isEmpty)
? "Transaction ID is required"
: null,
),
const SizedBox(height: 16),
fieldLabel("Payment Status", requiredMark: true),
buildDropdownHint(
hint: "Select status",
items: const ["Pending", "Completed", "Failed"],
value: paymentStatus,
onChanged: (val) => setState(() => paymentStatus = val),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF8270DB),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
padding: const EdgeInsets.symmetric(vertical: 14),
foregroundColor: Colors.white,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
// TODO: Save handler
}
},
child: const Text("Save",
style: TextStyle(fontSize: 16)),
),
),
],
),
),
),
),
),
),
),
);
}
Widget buildDropdownHint({
required String hint,
required List<String> items,
required String? value,
required Function(String?) onChanged,
TextStyle? hintStyle,
EdgeInsetsGeometry? contentPadding,
bool centerHint = false,
}) {
return DropdownButtonFormField<String>(
value: value,
// must be null to show the hint
isExpanded: true,
isDense: true,
decoration: InputDecoration(
// don't set hintText here; use the `hint:` widget below
contentPadding: contentPadding ??
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
),
hint: Align(
alignment: centerHint ? Alignment.center : Alignment.centerLeft,
child: Text(
hint,
textAlign: centerHint ? TextAlign.center : TextAlign.start,
style: hintStyle ??
fontTextStyle(
14,
const Color(0xFF939495),
FontWeight.w400),
),
),
items:
items.map((e) => DropdownMenuItem(value: e, child: Text(e))).toList(),
onChanged: onChanged,
dropdownColor: Colors.white,
validator: (v) => (v == null || v.isEmpty) ? "Required" : null,
);
}
Widget fieldLabel(String text, {bool requiredMark = false}) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(
text,
style: fontTextStyle(12, Color(0xFF515253), FontWeight.w500),
),
if (requiredMark) ...[
const SizedBox(width: 2),
Text("*",
style: fontTextStyle(
12, const Color(0xFFD32F2F), FontWeight.w800)),
],
],
),
);
}
Widget buildDropdown({
required String label,
required List<String> items,
required String? value,
required Function(String?) onChanged,
}) {
return DropdownButtonFormField<String>(
value: value,
decoration: InputDecoration(labelText: label),
isExpanded: true,
items:
items.map((e) => DropdownMenuItem(value: e, child: Text(e))).toList(),
onChanged: onChanged,
dropdownColor: Colors.white,
validator: (v) => (v == null || v.isEmpty) ? "Required" : null,
);
}
Widget buildTextField({
required String hint,
required TextEditingController controller,
TextInputType? keyboardType,
String? Function(String?)? validator,
// Optional style overrides
TextStyle? textStyle,
TextStyle? hintStyle,
EdgeInsetsGeometry? contentPadding,
Widget? prefixIcon,
Widget? suffixIcon,
bool obscureText = false,
int? maxLines = 1,
int? minLines,
int? maxLength,
}) {
return TextFormField(
controller: controller,
keyboardType: keyboardType,
validator: validator,
obscureText: obscureText,
maxLines: maxLines,
minLines: minLines,
maxLength: maxLength,
// Text input styling
style: textStyle ??
fontTextStyle(14, const Color(0xFF101214), FontWeight.w500),
cursorColor: const Color(0xFF8270DB),
decoration: InputDecoration(
hintText: hint,
hintStyle: hintStyle ??
fontTextStyle(13, const Color(0xFF6B7280), FontWeight.w500),
contentPadding: contentPadding ??
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,
// Border/fill are already controlled by your Theme.inputDecorationTheme
// but you could override here if needed.
),
);
}
Widget buildDatePicker({
required String hint,
TextStyle? hintStyle, // ← NEW
EdgeInsetsGeometry? contentPadding, // optional
TextAlign textAlign = TextAlign.start, // optional (for centering)
}) {
return TextFormField(
controller: dateController,
readOnly: true,
textAlign: textAlign,
decoration: InputDecoration(
hintText: hint,
hintStyle: hintStyle ??
fontTextStyle(
14,
const Color(0xFF939495),
FontWeight.w400),
contentPadding: contentPadding ??
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
suffixIcon: const Icon(Icons.calendar_today),
),
validator: (v) =>
(v == null || v
.trim()
.isEmpty) ? "Date is required" : null,
onTap: () async {
final picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
builder: (context, child) {
return Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: Colors.white,
colorScheme: Theme
.of(context)
.colorScheme
.copyWith(
primary: const Color(0xFF8270DB),
surface: Colors.white,
onSurface: const Color(0xFF101214),
),
),
child: child!,
);
},
);
if (picked != null) {
setState(() {
dateController.text =
"${picked.day.toString().padLeft(2, '0')}-"
"${picked.month.toString().padLeft(2, '0')}-"
"${picked.year}";
});
}
},
);
}
}