// This Flutter file defines a "Get Support" screen with various interactive UI elements. // It includes reusable widgets for displaying support options, FAQ items, a custom AppBar, // and reusable separator widgets. // The screen allows users to navigate to dummy pages for demonstration purposes. // It uses the 'google_fonts' package for custom typography (Poppins). import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; // --- Dummy Page for Navigation --- /// A simple [StatelessWidget] used as a placeholder page for navigation demonstrations. /// It displays a title in the AppBar and a message in the body, both indicating the page's purpose. class DummyPage extends StatelessWidget { /// The title to be displayed in the AppBar and body of the dummy page. final String title; /// Creates a [DummyPage]. /// /// The [title] parameter is required and will be used to identify the page. const DummyPage({super.key, required this.title}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( title, style: GoogleFonts.poppins(color: Colors.white), // Using Poppins font ), backgroundColor: Colors.deepOrange, // A distinct color for the dummy page AppBar iconTheme: const IconThemeData( color: Colors.white, ), // Ensures back arrow is white ), body: Center( child: Text( 'This is the $title page.', style: GoogleFonts.poppins(fontSize: 20), // Using Poppins font ), ), ); } } // --- Reusable UI Components --- /// A [StatelessWidget] that represents a circular support option with an icon and a label. /// Tapping on this widget triggers an [onTap] callback. /// /// This widget is used to display quick actions like "Raise a Ticket" or "Request a Callback". /// It features a circular gradient background for the icon and a text label below it. class SupportOption extends StatelessWidget { /// The icon to display within the circular background. final IconData iconData; /// The text label displayed below the icon. Can contain newlines for multi-line labels. final String label; /// The background color of the circle. /// Note: This is currently overridden by a `BoxDecoration` with a gradient in the implementation. /// It's kept for potential future use or simpler styling options. final Color circleBackgroundColor; /// The color of the icon. final Color iconColor; /// The callback function that is called when the widget is tapped. final VoidCallback onTap; /// The size of the icon. final double iconSize; /// The optional [TextStyle] for the label. If null, a default Poppins style is used. final TextStyle? labelStyle; /// Creates a [SupportOption] widget. /// All parameters are required except [iconColor], [iconSize], and [labelStyle], which have defaults. const SupportOption({ super.key, required this.iconData, required this.label, required this.circleBackgroundColor, required this.onTap, this.iconColor = Colors.white, this.iconSize = 25, this.labelStyle, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, // Makes the entire widget tappable child: Column( mainAxisSize: MainAxisSize .min, // Ensures the Column takes minimum vertical space needed by its children children: [ // Circular icon container Container( width: 56, // Fixed width for the circle height: 56, // Fixed height for the circle, ensuring it's perfectly round padding: const EdgeInsets.all( 12, ), // Padding around the icon inside the circle decoration: BoxDecoration( shape: BoxShape.circle, // Makes the container circular gradient: const LinearGradient( // Applies a gradient background to the circle begin: Alignment.bottomLeft, end: Alignment.topRight, colors: [ Color(0xFFF57C00), Color(0xFFFFB74D), ], // Orange gradient ), ), child: Icon( iconData, color: iconColor, size: iconSize, ), // The icon itself ), const SizedBox(height: 8), // Spacing between the icon and the label // Label text Text( label, textAlign: TextAlign.center, // Ensures multi-line labels are centered style: labelStyle ?? // Use custom style if provided, otherwise default GoogleFonts.poppins(fontSize: 13, fontWeight: FontWeight.w500), ), ], ), ); } } /// A [StatelessWidget] that represents an item in a Frequently Asked Questions (FAQ) list. /// It is typically used within a [ListView] or [Column] to display individual FAQ entries. /// Features include a leading icon in a [CircleAvatar], a text title, and an optional trailing widget. class FaqItem extends StatelessWidget { /// The icon to display at the beginning (leading edge) of the FAQ item. final IconData icon; /// The color of the leading icon. This color is also used to derive the /// semi-transparent background color of the [CircleAvatar] holding the icon. final Color iconColor; /// The main text or title of the FAQ item. final String text; /// The callback function that is called when the FAQ item is tapped. final VoidCallback onTap; /// The optional [TextStyle] for the main text. If null, a default Poppins style is used. final TextStyle? textStyle; /// An optional widget to display at the end (trailing edge) of the FAQ item, /// commonly an arrow icon indicating navigability. final Widget? trailing; /// The optional size for the leading icon. /// A special condition exists for `Icons.payment` with `Colors.green` to use a different default size. final double? iconSize; /// The optional radius for the [CircleAvatar] that contains the leading icon. final double? avatarRadius; /// Creates an [FaqItem] widget. /// The [icon], [iconColor], [text], and [onTap] parameters are required. const FaqItem({ super.key, required this.icon, required this.iconColor, required this.text, required this.onTap, this.textStyle, this.trailing, this.iconSize, this.avatarRadius, }); @override Widget build(BuildContext context) { // This condition allows for specific icon sizing if needed. // For more complex scenarios, this logic might be better handled by passing exact sizes. final bool isPaymentsIcon = icon == Icons.payment && iconColor == Colors.green; return ListTile( // `contentPadding` controls the internal padding of the ListTile. // `left: 16.0` provides standard leading padding. // `right: 16.0` provides padding on the right, ensuring the trailing widget // (if any) isn't flush against the screen edge. contentPadding: const EdgeInsets.only(left: 16.0, right: 16.0), leading: CircleAvatar( backgroundColor: iconColor.withOpacity( 0.15, ), // Background for the icon, derived from iconColor with reduced opacity radius: avatarRadius ?? 10, // Default radius is 10, can be overridden child: Icon( icon, color: iconColor, size: iconSize ?? (isPaymentsIcon ? 10 : 12), // Default size logic, can be overridden ), ), title: Text( text, style: textStyle ?? // Use custom style if provided, otherwise default GoogleFonts.poppins( fontWeight: FontWeight.w600, // Medium-bold weight for emphasis ), maxLines: 1, // Ensures the FAQ title is on a single line overflow: TextOverflow.ellipsis, // Displays "..." if the text overflows ), onTap: onTap, // Makes the ListTile tappable trailing: trailing, // The widget displayed at the end of the ListTile ); } } // --- New Reusable Component Structures for GetSupportScreen --- /// A reusable [AppBar] widget designed for this application, featuring a /// consistent gradient background, title styling, and icon theming. /// It implements [PreferredSizeWidget] which is necessary for a widget to be used /// as an [AppBar] in a [Scaffold]. class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { /// The text to be displayed as the title of the AppBar. final String titleText; /// An optional widget to display before the title, typically an [IconButton] for navigation (e.g., back button). final Widget? leading; /// An optional list of widgets to display after the title, typically [IconButton]s for actions. final List? actions; /// Creates a [CustomAppBar]. /// The [titleText] is required. const CustomAppBar({ super.key, required this.titleText, this.leading, this.actions, }); @override Widget build(BuildContext context) { return AppBar( leading: leading, actions: actions, // `flexibleSpace` allows a widget to be placed behind the AppBar's primary content (title, actions). // Here, it's used to create the gradient background. flexibleSpace: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, // Gradient starts from the left end: Alignment.centerRight, // Gradient ends at the right colors: [ // Defines the colors for the gradient Color(0xFFF57C00), // Darker orange Color(0xFFFFB74D), // Lighter orange (middle color) Color(0xFFF57C00), // Darker orange again for a symmetrical feel ], stops: [ // Defines the distribution of colors along the gradient line 0.0, // Start color at the beginning 0.5, // Middle color at the midpoint 1.0, // End color at the end ], ), ), ), backgroundColor: Colors .transparent, // Makes the AppBar's own background transparent, so the `flexibleSpace` gradient is visible. elevation: 0, // Removes the default shadow under the AppBar for a flatter look. title: Text( titleText, style: GoogleFonts.poppins( color: Colors.white, // Title text color fontWeight: FontWeight.bold, // Bold weight for the title ), ), centerTitle: true, // Centers the title horizontally within the AppBar. iconTheme: const IconThemeData( color: Colors .white, // Ensures that all icons in the AppBar (leading, actions) are white by default. ), ); } /// Defines the preferred size (height) of this AppBar. /// `kToolbarHeight` is a Flutter constant representing the standard height of an AppBar. @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); } /// A data class designed to hold the configuration for a single [SupportOption] widget. /// Using data classes like this helps in separating the data/content from the UI structure, /// making it easier to manage lists of similar items and pass data to reusable sections. class SupportOptionData { final IconData iconData; final String label; final VoidCallback onTap; SupportOptionData({ required this.iconData, required this.label, required this.onTap, }); } /// A reusable [StatelessWidget] that displays a horizontal row of [SupportOption] widgets. /// It takes a list of [SupportOptionData] to dynamically configure each option in the row. /// This component is used for the top section of the "Get Support" screen. class SupportOptionsSection extends StatelessWidget { /// A list of [SupportOptionData] objects, each defining the properties for one support option. final List options; /// Creates a [SupportOptionsSection]. /// The [options] list is required. const SupportOptionsSection({super.key, required this.options}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only( top: 20.0, // Space above the entire section left: 8.0, // Horizontal padding for the section content right: 8.0, ), // `LayoutBuilder` can be used for more complex responsive layouts, // but here `IntrinsicHeight` and `Row` with `Flexible` children handle the distribution. child: LayoutBuilder( builder: (context, constraints) { return IntrinsicHeight( child: Wrap( alignment: WrapAlignment.spaceBetween, spacing: 12, // Horizontal space between items runSpacing: 16, // Vertical space between rows if wrapped children: options.map((optionData) { return SizedBox( width: (constraints.maxWidth / 4) - 12, // 4 items per row, adjust as needed child: SupportOption( iconData: optionData.iconData, label: optionData.label, circleBackgroundColor: const Color(0xFFF57C00), iconColor: Colors.white, onTap: optionData.onTap, ), ); }).toList(), ), ); }, ), ); } } /// A data class to hold the configuration for a single [FaqItem] displayed within an [FaqSection]. /// This includes information like the icon, color, text, tap action, and whether a larger /// separator should be used after this item. class FaqSectionItem { final IconData icon; final Color iconColor; final String text; final VoidCallback onTap; /// If true, the [FaqSection] will use the `largerSpaceSeparator` (if provided) after this item. /// Otherwise, it will use the `defaultSeparator` (if provided). final bool useLargerSeparatorAfter; FaqSectionItem({ required this.icon, required this.iconColor, required this.text, required this.onTap, this.useLargerSeparatorAfter = false, // Defaults to using the smaller/default separator }); } /// A reusable [StatelessWidget] that displays a section for Frequently Asked Questions. /// It includes a section title and a list of [FaqItem] widgets. /// The separators between FAQ items are passed in as parameters, allowing for flexible styling. class FaqSection extends StatelessWidget { /// The title text for the FAQ section (e.g., "Frequently Asked Questions (FAQs)"). final String sectionTitle; /// A list of [FaqSectionItem] objects, each defining one FAQ entry. final List items; /// The size of the icon within each [FaqItem]. Defaults to 12. final double faqIconSize; /// The radius of the [CircleAvatar] within each [FaqItem]. Defaults to 12. final double faqAvatarRadius; /// The widget to use as a default separator between FAQ items. /// If null, `SizedBox.shrink()` is used, effectively showing no separator. final Widget? defaultSeparator; /// The widget to use as a separator when a larger space is desired between certain FAQ items /// (as indicated by `FaqSectionItem.useLargerSeparatorAfter`). /// If null, `SizedBox.shrink()` is used. final Widget? largerSpaceSeparator; /// Creates an [FaqSection]. /// [sectionTitle] and [items] are required. /// Separator widgets are optional and can be customized by the parent. const FaqSection({ super.key, required this.sectionTitle, required this.items, this.faqIconSize = 12, this.faqAvatarRadius = 12, this.defaultSeparator, this.largerSpaceSeparator, }); @override Widget build(BuildContext context) { return Container( color: Colors.white, // Background color for the entire FAQ section padding: const EdgeInsets.only( top: 8.0, // Reduced top padding for the section bottom: 16.0, // Maintained bottom padding for the section ), child: Column( crossAxisAlignment: CrossAxisAlignment .start, // Aligns children (title, FAQ items list) to the left children: [ // Section Title Padding( padding: const EdgeInsets.only( left: 16.0, // Horizontal padding for the title right: 16.0, bottom: 16.0, // Space between the title and the first FAQ item (remains 16.0) ), child: Text( sectionTitle, style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: const Color.fromARGB( 255, 90, 92, 121, ), // A specific dark grey/blue color for the title ), ), ), // List of FAQ Items and their Separators Column( children: [ // Using a `for` loop with the spread operator (`...`) to dynamically build // the list of FaqItems and their corresponding separators. for (int i = 0; i < items.length; i++) ...[ FaqItem( // Create an FaqItem for the current data item icon: items[i].icon, iconColor: items[i].iconColor, text: items[i].text, onTap: items[i].onTap, iconSize: faqIconSize, // Use the passed-in or default icon size avatarRadius: faqAvatarRadius, // Use the passed-in or default avatar radius trailing: IconButton( // Standard trailing arrow icon for all FAQ items in this section padding: EdgeInsets .zero, // Removes default padding from IconButton to make it compact constraints: const BoxConstraints(), // Removes default size constraints, allowing icon size to dominate icon: Icon( Icons.arrow_forward_ios, // iOS-style forward arrow size: 11, // Specific small size for the arrow color: Colors .grey .shade600, // A medium grey color for the arrow ), onPressed: items[i] .onTap, // Assumes the arrow tap triggers the same action as the item tap ), ), // Conditionally add a separator after the current item, but not after the last item if (i < items.length - 1) items[i] .useLargerSeparatorAfter // Check if this item requires a larger separator ? (largerSpaceSeparator ?? const SizedBox.shrink()) // Use provided larger separator, or an empty box if null : (defaultSeparator ?? const SizedBox.shrink()), // Use provided default separator, or an empty box if null ], ], ), ], ), ); } } /// A reusable widget for a thick, styled divider. /// This is typically used to separate major content sections on a page. /// It provides consistent styling for such separators. class ThickSectionSeparator extends StatelessWidget { const ThickSectionSeparator({super.key}); @override Widget build(BuildContext context) { return const Padding( padding: EdgeInsets.symmetric( vertical: 18.0, ), // Vertical spacing around the divider child: Divider( thickness: 7, // A very thick line for a prominent separation color: Color( 0xFFF5F5F5, ), // A light grey color, often matching subtle background variations height: 1, // The actual height of the divider line itself. Padding controls the space around it. ), ); } } // --- Main Screen Widget --- /// The main screen widget for the "Get Support" feature. /// This screen is composed of several reusable sections: /// - A [CustomAppBar] for the top navigation bar. /// - A [SupportOptionsSection] displaying quick actions. /// - A [ThickSectionSeparator] visually dividing major parts of the screen. /// - An [FaqSection] listing frequently asked questions. /// /// This screen demonstrates how to compose a complex UI by combining smaller, reusable widgets /// and managing their data and styling. class GetSupportScreen extends StatelessWidget { const GetSupportScreen({super.key}); /// A helper method to navigate to a [DummyPage] with a given [title]. /// This method is passed as a callback to various interactive elements /// (like [SupportOptionData] and [FaqSectionItem]) to handle their tap actions. void _navigateToDummy(BuildContext context, String title) { Navigator.push( context, MaterialPageRoute(builder: (_) => DummyPage(title: title)), ); } @override Widget build(BuildContext context) { /* --- Data and Style Definitions for Reusable Sections --- * Define the data that will be passed to the reusable sections. * Also, define common styling elements like dividers here to be passed down. * This approach keeps the UI structure (the `build` method) cleaner by separating * data and specific styling configurations from the main widget tree composition. * For larger applications, these definitions might live in separate files or theme/style managers. */ // Common Divider Styles for the FAQ Section. // These are defined here and passed to the FaqSection widget, promoting reusability // and centralized style management for this screen. const double faqDividerThickness = 0.2; // MODIFIED: Changed to a darker grey for better visibility final Color faqDividerColor = Colors.grey.shade400; // (0xFFBDBDBD) - More visible than shade300 const double faqDividerIndent = 16.0; // Indent from left, aligns with FaqItem content const double faqDividerEndIndent = 16.0; // Indent from right, before FaqItem trailing icon final Widget defaultFaqSeparator = Divider( height: 10, // Total vertical space for this separator (line + spacing) thickness: faqDividerThickness, color: faqDividerColor, indent: faqDividerIndent, endIndent: faqDividerEndIndent, ); final Widget largerFaqSeparator = Divider( height: 16, // More vertical space for this separator thickness: faqDividerThickness, color: faqDividerColor, indent: faqDividerIndent, endIndent: faqDividerEndIndent, ); // Data for the Support Options Section final List supportOptions = [ SupportOptionData( iconData: Icons.article_outlined, label: 'Raise a Ticket', onTap: () => _navigateToDummy(context, 'Raise a Ticket'), ), SupportOptionData( iconData: Icons.phone_callback_outlined, label: 'Request a\nCallback', onTap: () => _navigateToDummy(context, 'Request a Callback'), ), SupportOptionData( iconData: Icons.headset_mic_outlined, label: 'Call Customer\nCare', onTap: () => _navigateToDummy(context, 'Call Customer Care'), ), SupportOptionData( iconData: Icons.more_horiz, label: 'View Raised\nTickets', onTap: () => _navigateToDummy(context, 'View Raised Tickets'), ), ]; // Data for the FAQ Section final List faqItems = [ FaqSectionItem( icon: Icons.local_fire_department, iconColor: Colors.orange, text: 'FREQUENTLY ASKED QUESTIONS', onTap: () => _navigateToDummy(context, 'Frequently Asked Questions'), // `useLargerSeparatorAfter` defaults to false, so a `defaultFaqSeparator` will be used. ), FaqSectionItem( icon: Icons.account_balance_wallet, iconColor: Colors.blue, text: 'ACCOUNT MANAGEMENT', onTap: () => _navigateToDummy(context, 'Account Management'), ), FaqSectionItem( icon: Icons.payment, iconColor: Colors.green, text: 'PAYMENTS', onTap: () => _navigateToDummy(context, 'Payments'), useLargerSeparatorAfter: true, // A `largerFaqSeparator` will be used after this item. ), FaqSectionItem( icon: Icons.monetization_on, iconColor: Colors.amber, text: 'CARDS', onTap: () => _navigateToDummy(context, 'Cards'), useLargerSeparatorAfter: true, ), FaqSectionItem( icon: Icons.account_box, iconColor: Colors.blue, text: 'LOOP LOANS', onTap: () => _navigateToDummy(context, 'LOOP LOANS'), useLargerSeparatorAfter: true, ), FaqSectionItem( icon: Icons.account_balance, iconColor: Colors.orange, text: 'LOOP OVERDRAFT', onTap: () => _navigateToDummy(context, 'LOOP OVERDRAFT'), useLargerSeparatorAfter: true, ), FaqSectionItem( icon: Icons.credit_card, iconColor: Colors.blue, text: 'LOOP FLEX (Buy Now Pay Later(BNPL))', onTap: () => _navigateToDummy( context, 'LOOP FLEX (Buy Now Pay Later(BNPL))', ), useLargerSeparatorAfter: true, ), FaqSectionItem( icon: Icons.account_balance, iconColor: Colors.green, text: 'LOOP GOALS', onTap: () => _navigateToDummy(context, 'LOOP GOALS'), // For the last item, `useLargerSeparatorAfter` is effectively ignored as no separator is drawn after the last item. ), ]; // The main Scaffold for the GetSupportScreen return Scaffold( appBar: CustomAppBar( // Using the reusable custom AppBar titleText: 'Get Support', leading: IconButton( // Providing a custom leading widget (back button) icon: const Icon( Icons.arrow_back_ios, // iOS-style back arrow ), onPressed: () { // Navigate to a generic "Back Page" for demonstration purposes Navigator.of(context).push( MaterialPageRoute( builder: (_) => const DummyPage(title: 'Back Page'), ), ); }, ), ), // `SingleChildScrollView` allows the content within its `child` (the Container and Column) // to be scrollable if the content's height exceeds the available screen height. body: SingleChildScrollView( child: Center( // Center the constrained content horizontally child: ConstrainedBox( constraints: const BoxConstraints( // Set the minimum width to 600 maxWidth: 600, // Optionally, also set the maxWidth to 600 for a fixed width ), child: Container( color: Colors.white, child: Column( // Arranges the main sections of the screen vertically children: [ // First major section: Support Options SupportOptionsSection(options: supportOptions), // Reusable Thick divider separating the support options from the FAQ section const ThickSectionSeparator(), // Second major section: Frequently Asked Questions FaqSection( sectionTitle: 'Frequently Asked Questions (FAQs)', items: faqItems, defaultSeparator: defaultFaqSeparator, // Pass the defined default separator style largerSpaceSeparator: largerFaqSeparator, // Pass the defined larger separator style // `faqIconSize` and `faqAvatarRadius` will use their default values as defined in `FaqSection`, // but they could be overridden here if needed for this specific instance of FaqSection. // e.g., faqIconSize: 14, ), ], ), ), ), ), ), ); } }