Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

flutterlogo

Material Components Widgets


Material Components Widgets was completed by TheGarage mobile application development team. It is built using Flutter. The team used Getx framework for state management. Additionally, The primary objective of writing widget content is to ensure it results in clean code that is easy to read, maintain, understand, and modify by following a structured and consistent format.


🗺️ Contents

Material Components are a collection of UI widgets and design guidelines, divided into six components:

  • Actions : Interactive elements which trigger actions when clicked .
  • Communication : Used to convey information to the user.
  • Containment : Components for grouping in defined area.
  • Navigation : User movement within the application.
  • Selection : That allow users to make decisions .
  • Text Inputs :Elements for entering text .

action

Common Buttons

Elevated Button


Theme

     elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            textStyle: const TextStyle(
              fontFamily: 'GE-SS-Two',
              fontWeight: FontWeight.w500,
              color: Colors.white,
              fontSize: 16,
            ),
            disabledBackgroundColor: Colors.orange.shade100,
            disabledForegroundColor: Colors.white,
            minimumSize: const Size(398, 48),
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
            elevation: 0.0,
            backgroundColor: Colors.orange,
          )
      ),

Component

  ElevatedButton(
           onPressed: () {
           },
           child: const Text('press here'),
         )

Filled Button



Theme

 filledButtonTheme: FilledButtonThemeData(
 style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.orange),
foregroundColor:  MaterialStatePropertyAll(Colors.white),
       )

Component

 FilledButton(
                 onPressed: () {},
                 child: const Text('press here'),
               ),

Outlined Button



Theme

      outlinedButtonTheme: OutlinedButtonThemeData(
          style: OutlinedButton.styleFrom(
            foregroundColor: Colors.orange,
            textStyle: const TextStyle(
              fontFamily: 'GE-SS-Two',
              fontWeight: FontWeight.w300,
              color: Colors.white,
              fontSize: 16,
            ),
            side: const BorderSide(color: Colors.orange),
            disabledBackgroundColor: Colors.orange.shade100,
            minimumSize: const Size(398, 48),
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
            elevation: 0.0,
            backgroundColor: Colors.transparent,
          )),

Component

OutlinedButton(

               onPressed:  ()  {}
               child: const Text('press here'),
             ),

Text Button



Theme

          textButtonTheme: TextButtonThemeData(
            style: TextButton.styleFrom(
              disabledBackgroundColor: Colors.orange.shade100,
              foregroundColor: Colors.orange,
            ),
          ),

Component

TextButton(
                 onPressed: () {},
                 child: Text(
                   'press here',
                 ),
               ),

Text Button



Theme

floatingActionButtonTheme: FloatingActionButtonThemeData(
       backgroundColor: Colors.orange,
       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
       elevation: 0.0,
     )

Component

FloatingActionButton(
             onPressed: () {},
             child: Icon(Icons.add),
           ),

Text Button



Theme

floatingActionButtonTheme: FloatingActionButtonThemeData(
       backgroundColor: Colors.orange,
       shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
       elevation: 0.0,
     )

Component

  FloatingActionButton.extended(
             onPressed: () {
             },
             icon: Icon(Icons.add),
             label: Text('EXTENDED'),
           ),

Text Button



Theme

              iconTheme: const IconThemeData(color: Colors.orange),

Component

IconButton(
             onPressed: () {},
             icon: const Icon(Icons.save),
           ),

Text Button



Theme

          segmentedButtonTheme: SegmentedButtonThemeData(
          style: ButtonStyle(
              backgroundColor: MaterialStateProperty.resolveWith<Color>(
            (Set<MaterialState> states) {
              if (states.contains(MaterialState.selected)) {
                return Colors.orange;
              }
              return Colors.white;
            },
          )),
        )

Controller

Set <String> selection = {'S','M','L'};
 List<ButtonSegment<String>> selectionSegment = [
  ButtonSegment<String>(value: 'small', label: Text('S')),
  ButtonSegment<String>(value: 'medium', label: Text('M')),
  ButtonSegment<String>(value: 'large', label: Text('L')),
];

 void updateSelection(Set<String> newSelection){
  selection = newSelection;
  update();
}

Component

GetBuilder<Controller>(builder: (controller) {
          return SegmentedButton<String>(
            segments: controller.selectionSegment,
            selected: controller.selection,
            onSelectionChanged: (Set<String> newSelection) {
              controller.updateSelection(newSelection);
            },
            multiSelectionEnabled: true,
            showSelectedIcon: true,
          );
        })

Text Button



Theme

            useMaterial3: true,
            toggleButtonsTheme: const ToggleButtonsThemeData(
              selectedColor: Colors.orange,
              fillColor: Colors.transparent,
              borderWidth: 0,
              borderColor: Colors.transparent,
            )),

Controller

 List<bool> get isSelectedList
  {
   return List.generate(2, (index) => selectedIndex == index);
 }

 selectIndex(int index) {
   selectedIndex = index;
   update();
 }

Component

 GetBuilder<ActionController>(builder: (controller) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              ToggleButtons(
                renderBorder: false,
                isSelected: controller.isSelectedList,
                onPressed: (int index) => controller.updateSelectIndex(index),
                constraints: BoxConstraints.expand(width: size.width * 0.45),
                children: [
                  ToggleButtonViewWidget( controller: controller, index: 0,title: 'Option1',),
                  ToggleButtonViewWidget( controller: controller, index: 1,title: 'Option2',)
                ],
              ),
              Divider(
                height: size.height * 0.001,
                thickness: 1,
                color: Colors.grey,
              ),
              SizedBox(
                height: size.height * 0.014,
              ),
              if (controller.selectedIndex == 0)
                  const Text(' widget 1')
              else
                const Text(' widget 2')
            ],
          );
        })
class ToggleButtonViewWidget extends StatelessWidget {
 const  ToggleButtonViewWidget({
    super.key,
    required this.controller,
    required this.index,
     required this.title,
  });

  final String title;
  final ActionController controller;
  final int index;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    final textTheme = Theme.of(context).textTheme;
    return Column(
      children: [
        TextButton(
          onPressed: () {
            controller.updateSelectIndex(index);
          },
          child: Text(
            title,
            style: textTheme.headlineSmall!.copyWith(
              color: controller.selectedIndex == index ? Colors.orange : null,
            ),
          ),
        ),
        Container(
          height: size.height * 0.003,
          width: controller.selectedIndex == index ? 97 : 0,
          decoration: buildBoxDecoration(),
        ),
      ],
    );
  }
}
BoxDecoration buildBoxDecoration() {
  return const BoxDecoration(
    borderRadius: BorderRadius.only(
        topLeft: Radius.circular(100), topRight: Radius.circular(100)),
    color: Colors.orange,
  );
}

communication

Badge



call

    child: GetBuilder<MyController>(
            builder: (controller) {
              return Badge(
                addClick: () {
                  myController.add();
                },
                subClick: () {
                  myController.sub();
                },
              );
            },
          ),

Component

class Badge extends StatelessWidget {
  final Function addClick;
  final Function subClick;

  const Badge({
    Key? key,
    required this.addClick,
    required this.subClick,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final myController = Get.find<MyController>();
    return Container(
      color: Colors.grey,
      child: Stack(
        children: [
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              IconButton(
                onPressed: myController.add,
                icon: const Icon(Icons.add_circle),
              ),
              IconButton(
                onPressed: myController.sub,
                icon: const Icon(Icons.remove_circle),
              ),
            ],
          ),
          if (myController.list.isNotEmpty)
            Positioned(
              top: 0,
              right: 3,
              child: Container(
                padding: const EdgeInsets.all(4),
                decoration: const BoxDecoration(
                  color: Colors.red,
                  shape: BoxShape.circle,
                ),
                child: Text(
                  Get.find<MyController>().list.length.toString(),
                  style: const TextStyle(color: Colors.white),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

Controller

using List to counting the number in Badges.

class MyController extends GetxController {
  List<String> list = [];

  void add() {
    list.add(' ');
    update();
  }

  void sub() {
    if (list.isNotEmpty) {
      list.removeLast();
      update();
    }
  }
}

Badge



Theme

progressIndicatorTheme: const ProgressIndicatorThemeData(
     linearMinHeight: 2.0,
     color: Colors.white,
   ),

Component

      GetBuilder<controller>(
               builder: (controller) {
                 return LinearProgressIndicator(
                  color: Colors.white,
                   value: controller.progress,
                 );
               },
             ),
   ElevatedButton(
               onPressed: () async {
                 await controller.startProgress();
               },
               child: const Text('test'),
             ),

Controller

 double progress = 0.0;
 int totalSteps = 100;

 Future<void> startProgress() async {
   for (int currentStep = 0; currentStep < totalSteps; currentStep++) {
     await Future.delayed(const Duration(milliseconds: 100));
     progress = (currentStep / totalSteps).toDouble();
     update();
   }
 }


Badge



Theme

progressIndicatorTheme: const ProgressIndicatorThemeData(
     linearMinHeight: 2.0,
     color: Colors.white,
   ),

Component

   GetBuilder<controller>(
               builder: (controller) {
                 return CircularProgressIndicator(
                  color: Colors.white,
                   value: controller.progress,
                 );
               },
             ),
ElevatedButton(
               onPressed: () async {
                 await controller.startProgress();
               },
               child: const Text('test'),
             ),

Controller

 double progress = 0.0;
 int totalSteps = 100;

 Future<void> startProgress() async {
   for (int currentStep = 0; currentStep < totalSteps; currentStep++) {
     await Future.delayed(const Duration(milliseconds: 100));
     progress = (currentStep / totalSteps).toDouble();
     update();
   }
 }
   

Badge



Theme

We recommend using the 'SnackBarWidget' if you have only one design.

Theme

  snackBarTheme: const SnackBarThemeData(
       actionTextColor: Colors.white,
       backgroundColor: Colors.white,
       contentTextStyle: TextStyle(color: Colors.white),
       elevation: 20
   ),

Component

   Get.snackbar(
         'Title',
         'test',
         snackPosition: SnackPosition.BOTTOM,
         backgroundColor: Colors.white,
         snackStyle: SnackStyle.FLOATING,
       );

Badge



Theme

We recommend using the "GlobalSnackBar" class throughout the entire project, especially if there are various designs for SnackBars such as warning, error, and success messages.

Theme

  snackBarTheme: const SnackBarThemeData(
       actionTextColor: Colors.white,
       backgroundColor: Colors.transparent,
       contentTextStyle: TextStyle(color: Colors.black),
       elevation: 20
   ),

Component

class GlobalSnackBar {
 const GlobalSnackBar();

 static warningSnackBar(
   String message,
 ) {
   Get.snackbar('', '',
       titleText: WarningSnackBar(
         message: message,
       ),
       snackStyle: SnackStyle.FLOATING,
       snackPosition: SnackPosition.BOTTOM,
       barBlur: 0.0);
 }

 static errorSnackBar(
   String message,
 ) {
   Get.snackbar('', '',
       titleText: ErrorSnackBar(
         message: message,
       ),
       snackStyle: SnackStyle.FLOATING,
       snackPosition: SnackPosition.BOTTOM,
       barBlur: 0.0);
 }

 static questionSnackBar(
   String message,
 ) {
   Get.snackbar('', '',
       titleText: QuestionSnackBar(
         message: message,
       ),
       snackStyle: SnackStyle.FLOATING,
       snackPosition: SnackPosition.BOTTOM,
       barBlur: 0.0);
 }

 static successSnackBar(
   String message,
 ) {
   Get.snackbar('', '',
       titleText: SuccessSnackBar(
         message: message,
       ),
       snackStyle: SnackStyle.FLOATING,
       snackPosition: SnackPosition.BOTTOM,
       barBlur: 0.0);
 }
}

Containment)

Filled Button



Theme

          dialogTheme: const DialogTheme(
          backgroundColor: Colors.white,
          shadowColor: Colors.black,
              shape:  RoundedRectangleBorder(
                  borderRadius: BorderRadius.all(
                    Radius.circular(12),
                  )
              ),
        )

Alert Dialog

Component

AlertDialog(
          title: const Text('AlertDialog Title'),
          content: const SingleChildScrollView(
            child: ListBody(
              children: <Widget>[
                Text('This is a demo alert dialog.'),
                Text('Would you like to approve of this message?'),
              ],
            ),
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('Approve'),
              onPressed: () {
              Get.back();
              },
            ),
          ],
        )

Dialog

Component

Dialog(
            child: SizedBox(
              height: 150,
              child: Column(
          children: [
              Align(
                alignment: Alignment.topRight,
                child: IconButton(
                    onPressed: () {
                      Get.back();
                    },
                    icon: const Icon(Icons.close_outlined)),
              )
          ],
        ),
            )
        )

Filled Button



Theme

         useMaterial3: true,
            bottomSheetTheme: const BottomSheetThemeData(
              backgroundColor: Colors.white,
              elevation: 1.0,
              modalElevation: 1.0,
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.only(topRight: Radius.circular(28.0),
                    topLeft: Radius.circular(28.0),
                  )
              ),
            )

Bottom Sheet

Component

ElevatedButton(
          child: const Text('showBottomSheet'),
          onPressed: () {
            showBottomSheet<void>(
              context: context,
              builder: (BuildContext context) {
                return  SizedBox(
                  height: 200,
                  width: Get.width,
                  child: const Column(
                    children: [
                      Text('Bottom Sheet'),
                    ],
                  ),
                );
              },
            );
          },
        ),

Modal Bottom Sheet

Component

ElevatedButton(
          child: const Text('showModalBottomSheet'),
          onPressed: () {
            showModalBottomSheet<void>(
              showDragHandle:true,
               useSafeArea : true,
              context: context,
              builder: (BuildContext context) {
                return const SizedBox(
                  height: 200,
                  child: Column(
                    children:[
                      Text('Modal Bottom Sheet'),
                    ],
                  ),
                );
              },
            );
          },
        ),

Filled Button



Theme

       splashColor: Colors.blue.withAlpha(30),
          cardTheme: const CardTheme(
            elevation: 0.0,
            color: Colors.white,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(4)),
            ),
          ),

Component

Card(
          clipBehavior: Clip.hardEdge,
          child: InkWell(
            onTap: () {

            },
            child: const SizedBox(
              width: 390,
              height: 100,
              child: Text('A card that can be tapped'),
            ),
          ),
        )

Filled Button



Theme

   dividerColor: greyCoffeeColor,

Component

const Divider(
 thickness: 2,
 ),

Filled Button



Theme

 listTileTheme: const ListTileThemeData(
       textColor: Colors.black,
     ),

Component

const ListTile(
          leading: CircleAvatar(child: Text('B')),
          title: Text('Headline'),
          subtitle: Text(
              'Longer supporting text to demonstrate how the text.'),
        ),

Filled Button



Component

GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
        childAspectRatio: 0.8,
        mainAxisSpacing: 8.0,
        crossAxisSpacing: 10.0,
      ),
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemCount: 4,
      itemBuilder: (_, index) {
        return Container(
          color: Colors.grey,
        );
      },
    );

Filled Button



Component

Container(
      padding: const EdgeInsets.symmetric(horizontal: 8),
      child: ListView.builder(
          shrinkWrap: true,
          itemCount: 3,
          itemBuilder: (BuildContext context, int index) {
        return Card(
          child: Container(
            color: Colors.grey,
            height: 150,
            width: 150,
          ),
        );}),
    );


navigation

Badge



Theme

        appBarTheme:  const AppBarTheme(
            titleTextStyle: TextStyle(
                fontFamily: 'Mona-Sans',
                fontWeight: FontWeight.w800,
                fontSize: 20,
                color: Colors.orange),
            systemOverlayStyle: SystemUiOverlayStyle(
              statusBarColor: Colors.transparent,
              statusBarBrightness: Brightness.light,
              statusBarIconBrightness: Brightness.dark, // For Android (dark icons)
            ),
            elevation: 0.5,
            backgroundColor: Colors.white,
            iconTheme: IconThemeData(color: Colors.grey)),

Component

class AppBarWidget extends StatelessWidget implements PreferredSizeWidget {
 const AppBarWidget({super.key});


 @override
 Size get preferredSize => const Size.fromHeight(60);

 @override
 Widget build(BuildContext context) {
   return AppBar(
        leading: IconButton(
          onPressed: () {},
          icon: const Icon(Icons.person),
        ),
        centerTitle: true,
        title: const Text('Title'),
        actions: [
          IconButton(
            onPressed: () {},
            icon: const Icon(Icons.menu),
          ),
        ],
      );
 }
}

BottomAppBar

Theme

     bottomAppBarTheme: const BottomAppBarTheme(
            color: Colors.white,
            elevation: 0.0,
            height: 12,
            shadowColor: Colors.black,
            // shape: storage
          ),

Component

BottomAppBar(
          child: Row(
            children: [
              IconButton(
                tooltip: 'Search',
                icon: const Icon(Icons.search),
                onPressed: () {},
              ),
              IconButton(
                tooltip: 'Favorite',
                icon: const Icon(Icons.favorite),
                onPressed: () {},
              ),
            ],
          ),
        )

Badge



Theme

     navigationBarTheme: NavigationBarThemeData(
         backgroundColor: Colors.white,
         elevation: 0.0,
         shadowColor: Colors.grey.shade400,
         indicatorColor: Colors.blue)

Component

NavigationBar(
         selectedIndex: controller.pageIndex.value,
         onDestinationSelected: (int index) {

           controller.changeIndex(index);
         },
         destinations: [
           NavigationDestination(icon: Icon(Icons.abc), label: 'option1'),
           NavigationDestination(icon: Icon(Icons.abc), label: 'option2'),
           NavigationDestination(icon: Icon(Icons.abc), label: 'option3')
         ],
),
body: <Widget>[
         Container(
           alignment: Alignment.center,
           child: const Text('Page 1'),
         ),
         Container(
           alignment: Alignment.center,
           child: const Text('Page 2'),
         ),
         Container(
           alignment: Alignment.center,
           child: const Text('Page 3'),
         ),
       ][controller.pageIndex],

Controller

var pageIndex = 0;
 changeIndex(int index) {
   pageIndex = index;
 }

Badge



Theme

navigationDrawerTheme: NavigationDrawerThemeData(
           backgroundColor: Colors.blue.shade200,
           tileHeight: 12,
           shadowColor: Colors.black,
           indicatorColor: Colors.blue.shade50,
           indicatorShape: storage,
           labelTextStyle: MaterialStatePropertyAll(TextStyle(fontFamily: fontFamily))
         )

Component

 Scaffold(
       drawer: Drawer(
         child: ListView(
           children: [
             DrawerHeader(child: Text('Header')),
             ListTile(
               title: Text('option1'),
               onTap: () {},
             ),
             ListTile(
               title: Text('option2'),
               onTap: () {},
             ),
           ],
         ),
       ),
),

Badge



Theme

navigationRailTheme: NavigationRailThemeData(
           backgroundColor: Colors.white,
           elevation: 0,
           minWidth: 12,
           indicatorColor: Colors.blue,
           useIndicator: true
         )

Component

Row(
             children: [
               NavigationRail(
                 destinations: <NavigationRailDestination>[
                   NavigationRailDestination(
                     icon: Icon(Icons.favorite_border),
                     selectedIcon: Icon(Icons.favorite),
                     label: Text('First'),
                   ),
                   NavigationRailDestination(
                     icon: Icon(Icons.bookmark_border),
                     selectedIcon: Icon(Icons.book),
                     label: Text('Second'),
                   ),
                 ],
                 selectedIndex: controller.index,
                 onDestinationSelected: (int index) {
                   controller.changeIndex(index);
                 },
               )
             ],
           )

Controller

 var index = 0;

 changeIndex(int index) {
   index = index;
 }

tabBarTheme



Theme

tabBarTheme: TabBarTheme(
       labelColor: Colors.white,
       indicatorColor: Colors.blue.shade800,
       overlayColor: MaterialStateProperty.all(Colors.blue.shade300),
     )

Component

DefaultTabController(
     initialIndex: 1,
     length: 3,
     child:Scaffold(
       appBar: AppBar(
         bottom:TabBar(
           tabs:[
           Tab(text:'Car'),
           Tab(text:'Plane'),
           Tab(text:'Boat'),
           ],
         ),
       ),
       body:TabBarView(
         children:[
           CarWidget(),
           PlaneWidget(),
           BoatWidget(),
         ],
       ),
     ),
),

selection

Filled Button



Theme

    checkboxTheme: CheckboxThemeData(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(5),
            ),
            checkColor: MaterialStateProperty.all(Colors.white),
            fillColor: MaterialStateProperty.all(Colors.transparent),
          ),

Component

 GetBuilder<Controller>(builder: (controller) {
    return Checkbox(
    activeColor: Colors.orange,
    value: controller.isCheck,
    onChanged: (bool? value) {
    controller.updateCheckBox(value!);
    },
    );
    });

Controller

 bool isCheck = false;

 void updateCheckBox(bool value) {
   isCheck = value;
   update();
 }

Filled Button



Theme

 chipTheme: ChipThemeData(
     secondarySelectedColor: whiteColor,
     secondaryLabelStyle: const TextStyle(color: primaryOrangeColor),
     backgroundColor: whiteColor,
     disabledColor: whiteColor,
     selectedColor: whiteColor,
     shape: RoundedRectangleBorder(
       borderRadius: BorderRadius.circular(12),
       side: const BorderSide(
         color: greySteelColor,
       ),
     ),
   ),

Component

ChoiceChip(
         selectedColor: whiteColor,
         padding: const EdgeInsets.symmetric(horizontal: 16.0),
         label: const Text('item'),
         // selected chip value
         selected: _value == index,
         // onSelected method
         onSelected: (bool selected) {},
),

Filled Button



data picker

Theme

theme: ThemeData( // useMaterial3: true, primarySwatch: Colors.green, datePickerTheme: const DatePickerThemeData( headerForegroundColor: Colors.black, headerBackgroundColor: Colors.green, backgroundColor: Colors.white, rangePickerBackgroundColor: Colors.white, rangePickerHeaderBackgroundColor: Colors.green, rangePickerHeaderForegroundColor: Colors.black, ), ),


### Component

```bash
Column(
         mainAxisAlignment: MainAxisAlignment.spaceBetween,
         children: [
           controller.selectedDateRange == null
               ? const Center(
                   child: Text('Press the button to show the picker'),
                 )
               : Padding(
                   padding: const EdgeInsets.all(30),
                   child: Column(
                     crossAxisAlignment: CrossAxisAlignment.start,
               children: [
               controller.selectedDateRange?.start == controller.selectedDateRange?.end
                           ?
               // select only one date
                   Text(
                  " date: ${controller.selectedDateRange?.start.toString().split(' ')[0]}",
                  style: const TextStyle(
                 fontSize: 24, color: Colors.blue),
                             )
                 :
                // select multible date
              Column(
                      children: [
                         Text(
                          "Start date: ${controller.selectedDateRange?.start.toString().split(' ')[0]}",
                     style: const TextStyle( fontSize: 24, color: Colors.blue), ),
                     Text( "End date: ${controller.selectedDateRange?.end.toString().split(' ')[0]}",
                style: const TextStyle( fontSize: 24, color: Colors.red)),
                               ],
                             ),
                     ],
                   ),
                 ),
           FloatingActionButton(
             onPressed: () {
               controller.dateTimeRangePicker(context);
             },
             child: const Icon(Icons.date_range),
           )
         ]);

Controller

 DateTimeRange? selectedDateRange;

  void updateNewDateRange(DateTimeRange? newDate) 
  {
    selectedDateRange = newDate;
    update();
  }

  Future dateTimeRangePicker() async 
  {
    DateTimeRange? picked = await showDateRangePicker(
      context: Get.context!,
      firstDate: DateTime.now(),
      lastDate: DateTime(DateTime.now().year + 5),
      // if you need to control size of datapicker use this :
      // builder: (context, child) {
      //   return Center(
      //     child: ConstrainedBox(
      //       constraints: const BoxConstraints(maxWidth: 400.0, maxHeight: 560),
      //       child: child,
      //     ),
      //   );
      // },
    );
    if (picked == null) return;
    updateNewDateRange(picked);
    // if you want use time picker use this : 
    final initialTime = picked == null
        ? TimeOfDay(hour: DateTime.now().hour, minute: DateTime.now().minute)
        : TimeOfDay(
            hour: picked.duration.inHours, minute: picked.duration.inMinutes);
    // ignore:
    final newTime = await showTimePicker(
      context: Get.context!,
      initialTime: initialTime,
    );
    if (newTime == null) return;
    DateTime(
        picked.start.year, picked.start.month, newTime.hour, newTime.minute);
  }

This is with Package:

Package

 syncfusion_flutter_datepicker: ^22.1.37

Component

class CalendarWidget extends StatelessWidget {
 final DateRangePickerSelectionChangedCallback? onSelectionChanged;
 final DateRangePickerSelectionMode selectionMode;

const CalendarWidget(
     {super.key,
     required this.onSelectionChanged,
     required this.selectionMode});

 @override
 Widget build(BuildContext context) {
   final textTheme = Theme.of(context).textTheme;
   return SfDateRangePicker(
     minDate: DateTime.now().subtract(const Duration(days: -1)),
     view: DateRangePickerView.month,
     monthViewSettings: DateRangePickerMonthViewSettings(
         viewHeaderStyle: DateRangePickerViewHeaderStyle(
             textStyle: textTheme.bodySmall?.copyWith(fontSize: 12))),
     headerStyle: DateRangePickerHeaderStyle(
       textStyle: textTheme.displaySmall,
     ),
     monthCellStyle: DateRangePickerMonthCellStyle(
         todayTextStyle: textTheme.displaySmall,
         textStyle: textTheme.headlineLarge?.copyWith(fontSize: 12)),
     yearCellStyle: DateRangePickerYearCellStyle(
         todayTextStyle: textTheme.displaySmall,
         textStyle: textTheme.displaySmall),
     todayHighlightColor: primaryOrangeColor,
     endRangeSelectionColor: primaryOrangeColor,
     rangeSelectionColor: primaryOrangeColor,
     selectionColor: primaryOrangeColor,
     startRangeSelectionColor: primaryOrangeColor,
     onSelectionChanged: onSelectionChanged,
     selectableDayPredicate: controller.disableDate,
     selectionMode: selectionMode,
   );
 }
}

Controller

 String? startDate;
 String? endDate;
 String? rangeDate = DateFormat('yyyy-MM-dd')
     .format(DateTime.now().toLocal().subtract(const Duration(days: -1)));

 void onSelectionChanged(DateRangePickerSelectionChangedArgs args) {
   if (args.value is PickerDateRange) {
     if (args.value.endDate == null ||
         args.value.startDate == args.value.endDate) {
       startDate =
           DateFormat('yyyy-MM-dd').format(args.value.startDate).toString();
       endDate =
           DateFormat('yyyy-MM-dd').format(args.value.startDate).toString();
       rangeDate = startDate;
     } else {
       startDate = DateFormat('yyyy-MM-dd').format(args.value.startDate);
       endDate = DateFormat('yyyy-MM-dd').format(args.value.endDate);
       rangeDate = '$startDate - $endDate';
     }
   }
   update();
 }

bool disableDate(DateTime day) {
   if ((day.isAfter(
     DateTime.now().subtract(
       const Duration(days: 0),
     ),
   ))) {
     return true;
   }
   return false;
 }

Filled Button



Package

 dropdown_button2: ^2.3.9

Component

DropdownButtonHideUnderline(child:
DropdownButton2(
           alignment: Alignment.centerRight,
           isExpanded: true,
           hint: const Text( "Chose ",
             style: TextStyle(
               fontSize: 16,
               fontWeight: FontWeight.w200,
                 color:Colors.grey ,
             ),
           ),
           items: controller.item
               .map((item) => DropdownMenuItem<String>(
                     value: item,
                     child: Align(
                       alignment: Alignment.centerRight,
                       child: Text(
                         item,
                         style: TextStyle(
                           fontSize: 16,
                           fontWeight: FontWeight.w200,
                           color:Colors.black ,
                         ),
                         overflow: TextOverflow.ellipsis,
                       ),
                     ),
                   ))
               .toList(),
           value: controller.selectedValue,
           onChanged: (value) {
             controller.updateSelectedValue(value.toString());
           },
           iconStyleData: const IconStyleData(
             icon: Visibility(
               visible: true,
               child: Icon(Icons.keyboard_arrow_down),
             ),
           ),
           buttonStyleData: ButtonStyleData(
             height: size.height * 0.06,
             width: size.width * 0.9,
             padding: const EdgeInsets.only(left: 14, right: 14),
             decoration: BoxDecoration(
                 borderRadius: const BorderRadius.only(
                     topRight: Radius.circular(5),
                     topLeft: Radius.circular(5),
                     bottomLeft: Radius.circular(0),
                     bottomRight: Radius.circular(0)),
                     color:Colors.white ,
           ),
           dropdownStyleData: DropdownStyleData(
             decoration: BoxDecoration(
             color:Colors.grey ,
             maxHeight: size.height * 0.9,
             width: size.width * 0.9,
           ),
           menuItemStyleData: const MenuItemStyleData(
             height: 40,
           ),
         ));

Controller

 List<String> items = ["Option1", "Opchion2", "Other"];
 UnmodifiableListView<String> get item => UnmodifiableListView(items);
 String? selectedValue;
 void updateSelectedValue(String value) {
 selectedValue = value;
 update();
 }

DropDownButtonFormField

Component

          GetBuilder<DropdownController>(builder: (_) {
                return Container(
                  width: 350,
                  child: DropdownButtonFormField<String>(
                    validator: (value) {
                      if (value == null) {
                        return 'Please select Value';
                      }
                      return null;
                    },
                    decoration: const InputDecoration(
                      border: OutlineInputBorder(),
                    ),
                    hint: Text('Enter Your Value'),
                    isExpanded: true,
                    icon: Icon(Icons.arrow_downward_outlined),
                    elevation: 4,
                    value: dropdownController.dropdownValue,
                    onChanged: (String? newValue) {
                      dropdownController.dropdownValue = newValue!;
                    },
                    onSaved: (value) {
                      dropdownController.dropdownValue = value.toString();
                    },
                    items: <String>['Value1', 'Value2', 'Value3', 'Value4']
                        .map<DropdownMenuItem<String>>((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(value),
                      );
                    }).toList(),
                  ),
                );
              }),

DropdownMenu

    return GetBuilder<DropdownController>(builder: (_) {
      return DropdownMenu<String>(
        hintText: 'Select',
        trailingIcon: Icon(Icons.arrow_drop_down_outlined),
        leadingIcon: Icon(Icons.calendar_month),
        width: 250,
        errorText: dropdownController.isOpen ? 'Error text' : null,
        onSelected: (String? value) {
          dropdownController.dropdownValue = value!;
        },
        dropdownMenuEntries: dropdownController.list
            .map<DropdownMenuEntry<String>>((String value) {
          return DropdownMenuEntry<String>(value: value, label: value);
        }).toList(),
      );
    });

Controller

  String? dropdownValue;
  bool isOpen = false;
  List<String> list = <String>['Value1', 'Value2', 'Value3', 'Value4'];
 

Filled Button



Theme

     radioTheme: RadioThemeData(
            overlayColor: MaterialStateProperty.all(Colors.grey),
            fillColor: MaterialStateProperty.all(Colors.transparent),
          ),

Component

     Column(
          children: [
            RadioButton(text: 'Option1',newValue: 1,),
            RadioButton(text: 'Option2',newValue: 2,),
            RadioButton(text: 'Option3',newValue: 3,),
          ],
        )
class RadioButton extends StatelessWidget {
  final String text;
  final int newValue;
  final selectionController = Get.put(SelectionController());

  RadioButton({
    super.key,
    required this.newValue,
    required this.text,
  });

  @override
  Widget build(BuildContext context) {
    return GetBuilder<SelectionController>(builder: (controller)
    {
      return Row(
        children: [
          SizedBox(
            width: 20,
            height: 20,
            child: Radio(
              value: newValue,
              groupValue: controller.radioButtonId,
              onChanged: (value) {
                controller.setSelectedFoodId(value = newValue);
                controller.updateSelected(text);
              },
              activeColor: Colors.orange,
            ),
          ),
          const SizedBox(
            width: 10,
          ),
          Text(
            text,
          ),
        ],
      );
    });
    }
  }

Controller

 int radioButtonId = 0;
 String selectedRadioButton = '';

 void updateSelected(String value) {
   selectedRadioButton = value;
   update();
 }

 void setSelectedFoodId(int radioButtonIdValue) {
   radioButtonId = radioButtonIdValue;
   update();
 }

Filled Button



Theme

 sliderTheme: const SliderThemeData(
        thumbColor: Colors.black,
        activeTrackColor: Colors.white,
        trackHeight: 5,
      )

Component

 Slider(
            value: controller.quality,
            max: 100,
            divisions: 5,
            onChanged: (double value) {
              controller.setquality(value);
      },
  )

Controller

  double quality = 0.0;

  void setquality(double value) {
    quality = value;
    update();
  }

Filled Button



Theme

switchTheme: SwitchThemeData(
     thumbColor: MaterialStateProperty.all(whiteColor),
     trackColor: MaterialStateProperty.all(blackColor),
     trackOutlineColor: MaterialStateProperty.all(primaryPurpleColor),
   ),

Component

  Switch(
         value: controller.isOpen,
         onChanged: (value) {
         controller.changeSwitch(value);
   },
 ),

Controller

 bool isOpen = false;

 void changeSwitch(bool value) {
   isOpen = value;
   update();
 }

Filled Button



Theme

Theme"
Theme"

Component

Component"
Component"

Controller

Controller"
Controller"

text_inputs

Text Inputs

TextField

Theme

       inputDecorationTheme: const InputDecorationTheme(
             contentPadding: EdgeInsets.symmetric(vertical: 2.0, horizontal: 10),
             fillColor: Color.fromRGBO(55, 64, 76, 0.05),
             prefixIconColor: Color.fromRGBO(55, 64, 76, 0.05),
             suffixIconColor: Color.fromRGBO(55, 64, 76, 0.05),
             focusedBorder: UnderlineInputBorder(
               borderSide: BorderSide(
                 width: 2,
                 color: Color.fromRGBO(1, 1, 1, 0.1),
               ),
             ),
             border: UnderlineInputBorder(
               borderSide: BorderSide(
                 width: 2,
               ),
             ),
             enabledBorder: UnderlineInputBorder(
               borderRadius: BorderRadius.only(
                   topRight: Radius.circular(5),
                   topLeft: Radius.circular(5),
                   bottomLeft: Radius.circular(0),
                   bottomRight: Radius.circular(0)),
               borderSide: BorderSide(
                 width: 2,
                 color: Color.fromRGBO(1, 1, 1, 0.1),
               ),
             ),
             filled: true,
           ),

Component

class TextFieldWidget extends StatelessWidget {
 final TextEditingController controller;
 final FormFieldValidator? validator;
 final Widget? prefixIcon, suffixIcon;
 final String? text , hintText;
 final bool? enabled;
 final TextInputType? keyboardType;
 final ValueChanged<String>? onChanged;
 final Iterable<String>? autofillHints;
 final List<TextInputFormatter>? inputFormatters;
 final int? maxLine , maxLength;

 const TextFieldWidget({
   required this.controller,
   this.validator,
   this.onChanged,
   this.prefixIcon,
   this.suffixIcon,
   this.enabled,
   this.hintText,
   this.text,
   this.keyboardType,
   Key? key,
   this.autofillHints,
   this.inputFormatters,
   this.maxLine,
   this.maxLength,
 }) : super(key: key);

 @override
 Widget build(BuildContext context) {
   final size = MediaQuery.of(context).size;
   final textTheme = Theme.of(context).textTheme;
   return Column(
     children: [
       Padding(
         padding: const EdgeInsets.only(
           right: 16,
         ),
         child: Align(
           alignment: Alignment.topRight,
           child: text == null
               ? null
               : Text(
                   '$text',
                   style: textTheme.bodyMedium,
                 ),
         ),
       ),
       const SizedBox(
         height: 8,
       ),
       TextFormField(
         inputFormatters: inputFormatters,
         autofillHints: autofillHints,
         enabled: enabled,
         style: textTheme.bodySmall,
         controller: controller,
         onChanged: onChanged,
         maxLines: maxLine,
         keyboardType: keyboardType,
         cursorColor: Colors.black,
         validator: validator,
         maxLength: maxLength,
         decoration: InputDecoration(
           hintText: hintText,
           hintStyle: textTheme.bodySmall,
           prefixIcon: prefixIcon,
           suffixIcon: suffixIcon,
         ),
       ),
     ],
   );
 }
}
TextFieldWidget(
           suffixIcon:const Icon(Icons.remove_red_eye_outlined),
           prefixIcon:const Icon(Icons.person),
           hintText: 'Hint Text',
           controller: textEditingController,
           maxLength: 20,
           validator: (value) {
// validation for test for GetUtils
             if (!GetUtils.isLengthBetween(value, 2, 10)) {
               return 'Letters must be more than two letters and less than 10';
             }
             return null;
           },
         ),
       ),

Search

Often we use a search bar in our app to perform autocomplete-style searches via network calls. In such a case, it is not feasible to perform a network request with every character that the user types/changes; that would result in wasted network calls. it is better to search once the user pauses or stops typing. This can be achieved using a Debouncer, which uses a timer to delay the search request until it stops receiving text changes for half a second, or any duration that you set.

You have to creat this class :

Delay Search

class DelaySearch {
final int milliseconds;
VoidCallback? action;
Timer _timer;

DelaySearch({required this.milliseconds})
    : _timer = Timer(Duration.zero, () {});

run(VoidCallback action) {
  _timer.cancel();
  _timer = Timer(Duration(milliseconds: milliseconds), action);
}
}

Component

final delaySearch = DelaySearch(milliseconds: 500);

TextFieldWidget(
                         onChanged: (value) {
                           delaySearch.run(() {
                             searchController
                                 .search(value);
                           });
                         },
                         hintText: 'Search Here',
                         suffixIcon: const Icon(Icons.search),
                         controller:
                             searchController.searchEditingController),
                   ),

Controller

 TextEditingController searchEditingController = TextEditingController();
 List<Model> searchList = [];

Others

Authentication

Controller

class AuthController extends GetxController {
 final AuthService service = AuthService();

 final TextEditingController nameController = TextEditingController();
 final TextEditingController emailController = TextEditingController();
 final TextEditingController phoneNumberController = TextEditingController();
 final TextEditingController passwordController = TextEditingController();

 final TextEditingController firstDigit = TextEditingController();
 final TextEditingController secondDigit = TextEditingController();
 final TextEditingController thirdDigit = TextEditingController();
 final TextEditingController fourthDigit = TextEditingController();
 final TextEditingController fifthDigit = TextEditingController();
 final TextEditingController sixthDigit = TextEditingController();

 bool isTimerDone = false;
 bool isValid = false;

 num otpDigitCount = 0;

 String otp = '';

 bool isVisibility = false;


void visibility() {
   isVisibility = !isVisibility;
   update();
 }

 void updateTimer(bool value) {
   isTimerDone = value;
   update();
 }

 void updateValidate(bool value) {
   isValid = value;
   update();
 }

 void updateOtp(int value, String opr) {
   if (opr == '+') {
     otpDigitCount += value;
   } else if (opr == '-') {
     otpDigitCount -= 1;
   }
   update();
 }

 void updateAutofill(String value) {
   otp = value;
   firstDigit.text = value[0];
   secondDigit.text = value[1];
   thirdDigit.text = value[2];
   fourthDigit.text = value[3];
   fifthDigit.text = value[4];
   sixthDigit.text = value[5];
   update();
 }

 void clearOTPController() {
   firstDigit.clear();
   secondDigit.clear();
   thirdDigit.clear();
   fourthDigit.clear();
   fifthDigit.clear();
   sixthDigit.clear();
   update();
 }
}

Login with phone

final _formKey = GlobalKey<FormState>();

Form(
         key: _formKey,
         child: Column(
           children: [
             TextFieldWidget(
               inputFormatters: [
                 FilteringTextInputFormatter.deny('+966')
               ],
               autofillHints: const [AutofillHints.telephoneNumber],
               text: 'Phone Number',
               prefixIcon: const Icon(
                 Icons.phone,
               ),
               keyboardType: TextInputType.number,
               controller: authController.phoneNumberController,
               hintText: '5xxxxxxxx',
             ),
             ElevatedButton(
               onPressed: () async {
                 if (_formKey.currentState!.validate()) {

                 }
               },
               child: Text('Enter'),
             ),
           ],
         )),

Login with email

final _formKey = GlobalKey<FormState>();


Form(
       key: _formKey,
       child:  Center(
         child: Column(
           children: [
             TextFieldWidget(
               maxLine: 1,
               controller: authController.emailController,
               prefixIcon: const Icon(
                 Icons.email_outlined,
               ),
             ),
             TextFieldWidget(
               maxLine: 1,
               controller: authController.passwordController,
               obscureText  :
               authController.isVisibility ? false : true,
               suffixIcon: IconButton(
                 onPressed: () {
                   authController.visibility();
                 },
                 icon: authController.isVisibility
                     ? const Icon(
                   Icons.visibility_off,
                 )
                     : const Icon(
                   Icons.visibility,
                 ),
               ),
               prefixIcon: const Icon(
                 Icons.lock,
               ),
             ),
             ElevatedButton(onPressed: (){
           if (_formKey.currentState!.validate()) {
            // The method here
                         }
             }, child: const Text('Enter'))
           ],
         ),
       ),
     ),

Sign Up with Email

final _formKey = GlobalKey<FormState>();
Form(
           key: formKey,
           child: Column(
               TextFieldWidget(
                 controller: authController.emailController,
                 obscureText: false,
                 prefixIcon: const Icon(Icons.email),
               ),
               TextFieldWidget(
                 controller: authController.nameController,
                 maxLine: 1,
                 obscureText: false,
                 validator: (value) {
                   if (value.toString().isEmpty) {
                     return 'Enter your Name';
                   } else if (!RegExp(Validation.validationName)
                       .hasMatch(value)) {
                     return "Please enter a correct Name";
                   } else if (value.toString().length < 2) {
                     return 'Your Name should be at least 2 long ';
                   } else {
                     return null;
                   }
                 },
                 prefixIcon: const Icon(Icons.person),
               ),
               GetBuilder<AuthController>(builder: (authController) {
                 return Column(
                   children: [
                     TextFieldWidget(
                       controller: authController.passwordController,
                       maxLine: 1,
                       obscureText:
                       authController.isVisibility ? false : true,
                       suffixIcon: IconButton(
                           onPressed: () {
                             authController.visibility();
                           },
                           icon: authController.isVisibility
                               ? const Icon(
                             Icons.visibility_off,
                           )
                               : const Icon(
                             Icons.visibility,
                           )),
                       prefixIcon: const Icon(
                         Icons.lock,
                       ),
                     ),
                     TextFieldWidget(
                       controller:
                       authController.checkPasswordController,
                       maxLine: 1,
                       obscureText:
                       authController.isVisibility ? false : true,
                       validator: (value) {
                         if (value.toString() !=
                             authController.passwordController.text) {
                           return 'Your password not corrected';
                         } else {
                           return null;
                         }
                       },
                       suffixIcon: IconButton(
                           onPressed: () {
                             authController.visibility();
                           },
                           icon: authController.isVisibility
                               ? const Icon(
                             Icons.visibility_off,
                           )
                               : const Icon(
                             Icons.visibility,
                           )),
                       prefixIcon: const Icon(
                         Icons.lock,
                       ),
                     ),
                   ],
                 );
               }),
               ElevatedButton(
                 onPressed: () async {
                   if (_formKey.currentState!.validate()) {
            
                   }
                 },
                 child: Text('Enter'),
               ),
             ],
           )),

OTP

class OtpScreen extends StatelessWidget {
 final _formKey = GlobalKey<FormState>();

 OtpScreen({Key? key}) : super(key: key);

 @override
 Widget build(BuildContext context) {
   final textTheme = Theme.of(context).textTheme;
   final size = MediaQuery.of(context).size;
   return Scaffold(
     appBar: AppBar(),
     body: GetBuilder<AuthController>(
       builder: (authController) {
         final otpDigits = [
           authController.firstDigit,
           authController.secondDigit,
           authController.thirdDigit,
           authController.fourthDigit,
           authController.fifthDigit,
           authController.sixthDigit,
         ];
         return SingleChildScrollView(
           physics: const NeverScrollableScrollPhysics(),
           child: Column(
             children: [
               if (authController.isValid)
                 const Align(
                     alignment: Alignment.centerRight,
                     child: Padding(
                       padding: EdgeInsets.all(16.0),
                       child: Text(
                         'You entered an incorrect number',
                         style: TextStyle(color: errorColor),
                       ),
                     )),
               Padding(
                 padding: const EdgeInsets.all(16.0),
                 child: Column(
                   children: [
                     SizedBox(
                       height: 40,
                       width: size.width * 0.95,
                       child: Directionality(
                         textDirection: TextDirection.ltr,
                         child: Form(
                           key: _formKey,
                           child: Center(
                             child: ListView.builder(
                               physics: const NeverScrollableScrollPhysics(),
                               shrinkWrap: true,
                               scrollDirection: Axis.horizontal,
                               itemCount: otpDigits.length,
                               itemBuilder: (context, index) {
                                 return OTPTextField(
                                   isFocused: true,
                                   textController: otpDigits[index],
                                   index: index,
                                 );
                               },
                             ),
                           ),
                         ),
                       ),
                     ),
                     SizedBox(
                       height: size.height * 0.025,
                     ),
                     ElevatedButton(
                         onPressed: authController.otp.length ==
                                 otpDigits.length
                             ? () async {
                                 authController.otp !=
                                         otpDigits
                                             .map((e) => e.text)
                                             .toList()
                                             .join()
                                     ? authController.updateValidate(true)
                                     : authController.updateValidate(false);
                                 if (!authController.isValid) {}
                               }
                             : null,
                         child: const Text('Next'))
                   ],
                 ),
               ),
               Row(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: [
                   authController.isTimerDone
                       ? TextButton(
                           style: TextButton.styleFrom(
                             disabledBackgroundColor: blackColor,
                             foregroundColor: blackColor,
                           ),
                           onPressed: () async {},
                           child: const Text('press here'))
                       : const Padding(
                           padding: EdgeInsets.all(2),
                           child: Text(
                                 'press here',
                             style: TextStyle(color: greyColor),
                           ),
                         ),
                   const Text('To send the code again')
                 ],
               ),
             ],
           ),
         );
       },
     ),
   );
 }
}

OTPTextField

class OTPTextField extends StatelessWidget {
 final _authController = Get.find<AuthController>();
 final TextEditingController textController;
 final bool isFocused;
 final int index;

 OTPTextField({
   super.key,
   required this.textController,
   required this.isFocused,
   required this.index,
 });

 @override
 Widget build(BuildContext context) {
   final size = MediaQuery.of(context).size;
   return Container(
     margin: EdgeInsets.symmetric(horizontal: size.width * 0.016),
     height: size.height * 0.0563,
     width: size.width*0.122,
     child: TextFormField(
       autofocus: isFocused,
       autofillHints: const [AutofillHints.oneTimeCode],
       inputFormatters: [
         if (index != 0) LengthLimitingTextInputFormatter(1),
         FilteringTextInputFormatter.allow(RegExp('[0-9]'))
       ],
       textAlign: TextAlign.center,
       keyboardType: TextInputType.number,
       controller: textController,
       cursorColor: blackColor,
       decoration: InputDecoration(
         border: OutlineInputBorder(
           borderRadius: BorderRadius.circular(4),
           borderSide: BorderSide(
             color: _authController.isValid ? errorColor : whiteColor,
           ),
         ),
         focusedBorder: OutlineInputBorder(
           borderRadius: BorderRadius.circular(4),
           borderSide: BorderSide(
             color: _authController.isValid ? errorColor : whiteColor,
           ),
         ),
         enabledBorder: OutlineInputBorder(
           borderRadius: BorderRadius.circular(4),
           borderSide: BorderSide(
             color: _authController.isValid ? errorColor : whiteColor,
           ),
         ),
       ),
       onChanged: (value) {
         if (value.isNotEmpty) {
           if (value.length == 6) {
             _authController.updateAutofill(value);
             FocusScope.of(context).unfocus();
           } else {
             if (index == 0) {
               _authController.firstDigit.text =
                   value.substring(value.length - 1);
             }
             _authController.updateOtp(value.length, '+');
             FocusScope.of(context).nextFocus();
           }
         } else {
           _authController.updateOtp(value.length, '-');
           FocusScope.of(context).previousFocus();
         }
       },
     ),
   );
 }
}

Skeleton

Controller

class SkeletonController extends GetxController with GetSingleTickerProviderStateMixin {
 
 late AnimationController _animationController;
 late Animation gradientPosition;

 @override
 onInit(){
   super.onInit();
   _animationSetup();
 }

 @override
 void dispose() {
   _animationController.dispose();
   super.dispose();
 }

 void _animationSetup() {
   _animationController = AnimationController(duration: const Duration(milliseconds: 1500), vsync: this);
   gradientPosition = Tween<double>(
     begin: -3,
     end: 10,
   ).animate(
     CurvedAnimation(parent: _animationController, curve: Curves.linear),
   )..addListener(update);
   _animationController.repeat();
 }
 
}

Component

class Skeleton extends StatelessWidget {
 final double height;
 final double width;
 final BorderRadiusGeometry? borderRadius;

 const Skeleton({
   Key? key,
   this.height = 20,
   this.width = 200,
   this.borderRadius,
 }) : super(key: key);

 @override
 Widget build(BuildContext context) {
   return GetBuilder<SkeletonController>(builder: (skeletonController) {
     return Container(
       width: width,
       height: height,
       decoration: BoxDecoration(
         borderRadius: borderRadius,
         gradient: LinearGradient(
           begin: Alignment(skeletonController.gradientPosition.value, 0),
           end: const Alignment(-1, 0),
           colors: const [greyPlatinumColor, greyMysticColor, greyLightColor],
         ),
       ),
     );
   });
 }
}

Timer

Controller

 Timer? countdownTimer;
 bool enableButton = true;
 bool isTimeOver = false
 
 
 ;
 Duration myduration = const Duration(minutes: 1);

 startTimer() {
   countdownTimer =
       Timer.periodic(const Duration(seconds: 1), (timer) => setCountDown());
   update();
 }

 String strDigits(int n) => n.toString().padLeft(2, '0');

 var hours = " ".obs;
 var minutes = " ".obs;
 var seconds = " ".obs;

 setCountDown() {
   const reduceBy = 1;
   final seconds = myduration.inSeconds - reduceBy;
   if (seconds < 0) {
     countdownTimer!.cancel();
     enableButton = true;
     isTimeOver = true;
     myduration = const Duration(minutes: 1);
     
   } else {
     myduration = Duration(seconds: seconds);
       enableButton = false;
     isTimeOver = false;
   }
   update();
 }

Component

controller.hours.value =
         controller.strDigits(controller.myduration.inHours.remainder(24));
     controller.minutes.value =
         controller.strDigits(controller.myduration.inMinutes.remainder(60));
     controller.seconds.value =
         controller.strDigits(controller.myduration.inSeconds.remainder(60));
          return Scaffold(

       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: <Widget>[
             ElevatedButton(
                 onPressed: controller.enableButton && !controller.isTimeOver || controller.enableButton && controller.isTimeOver
                     ? () {
                         controller.enableButton = false;
                         controller.isTimeOver = false;
                         controller.startTimer();
                       }
                     : null,
                 child: Text('Start Timer')),
             SizedBox(
               height: 12,
             ),
             Text(controller.enableButton && !controller.isTimeOver 
                 ? "Press the button to start the timer"
                 : controller.enableButton && controller.isTimeOver
                     ? "Time's up"
                     : "${controller.hours.value.toString()}:${controller.minutes.value.toString()}:${controller.seconds.value.toString()}"),
           ],
         ),
       ),
     );

MediaQuery

The Iphone 14 screen size in pixels is 844 x 390 :

   final size = MediaQuery.of(context).size;

Width

size.width

2 = size.width * 0.005

4 = size.width * 0.01

6 = size.width * 0.015

8 = size.width * 0.02

10 = size.width * 0.025

12 = size.width * 0.03

14 = size.width * 0.035

16 = size.width * 0.040

18 = size.width * 0.045

20 = size.width * 0.05

22 = size.width * 0.055

24 = size.width * 0.06

26 = size.width * 0.065

28 = size.width * 0.07

30 = size.width * 0.075

32 = size.width * 0.080

380 = size.width * 0.97

Height

size.height

2 = size.height * 0.0023

4 = size.height * 0.0047

6 = size.height * 0.007

8 = size.height * 0.0094

10 = size.height * 0.011

12 = size.height * 0.014

14 = size.height * 0.016

16 = size.height * 0.018

18 = size.height * 0.021

20 = size.height * 0.023

22 = size.height * 0.026

24 = size.height * 0.028

26 = size.height * 0.03

28 = size.height * 0.033

30 = size.height * 0.035

32 = size.height * 0.037

card slider

controller

class Controller extends GetxController {

 late PageController pageController;
 Timer? timer;
 List<String> adsList = <String>[AppImage.businessCardImage,AppImage.businessCardImage,AppImage.businessCardImage];

 @override
 void onInit() async
  {
   super.onInit();
   pageController = PageController(initialPage: 0, viewportFraction: 0.797);
 //  await allAds();
 }

 @override
 onReady() {
   var currentIndex = 0;
   timer = Timer.periodic
   (const Duration(seconds: 4), (timer) {
     if (pageController.hasClients) {
       if (currentIndex < adsList.length - 1) {
         currentIndex++;
         pageController.nextPage(
           duration: const Duration(milliseconds: 2000),
           curve: Curves.ease,
         );
       } else {
         if (pageController.hasClients) {
           currentIndex = 0;
           pageController.jumpToPage(0);
         }
       }
     }
   });
 }

Component

class CardWidget extends StatelessWidget 
{
 final String adsModel;

 CardWidget({
   Key? key,
   required this.adsModel,
 }) : super(key: key);

 @override
 Widget build(BuildContext context) 
 {
   final size = MediaQuery.of(context).size;
   return Container(
     margin: const EdgeInsets.symmetric(horizontal: 10),
     height: size.height * 0.149,
     child: GestureDetector(
       onTap: () {},
       child: SizedBox(
         height: size.height * 0.149,
         child: Container(
           width: size.width * 0.797,
           decoration: BoxDecoration(
             color: errorColor,
             borderRadius: const BorderRadius.all(
               Radius.circular(16),
             ),
             image: DecorationImage(
               image: AssetImage(AppImage.businessCardImage),
               fit: BoxFit.cover,
             ),
           ),
         ),
       ),
     ),
   );
 }
}

--------------------------

class CardListWidget extends StatelessWidget 
{
 const CardListWidget({
   Key? key,
 }) : super(key: key);

 @override
 Widget build(BuildContext context) 
 {
   final size = MediaQuery.of(context).size;
   return SizedBox
   (
     height: size.height * 0.149,
     child: GetBuilder<Controller>(
       builder: (controller) {
         return SizedBox(
           height: size.height * 0.215,
           child: PageView.builder
           (
             controller: controller.pageController,
             scrollDirection: Axis.horizontal,
             itemCount: controller.adsList.length,
             itemBuilder: (context, index) {
              var data = controller.adsList[index];
               return SizedBox(
                 height: size.height * 0.149,
                 width: size.width * 0.797,
                 child: CardWidget(
                   adsModel: data,
                 ),
               );
             },
           ),
         );
       },
     ),
   );
 }
}

Data Persistence

Firebase

https://github.com/The-Garage-Tech-Team/auth_crud_map_template

Local database (SQLite)

https://github.com/SirinK2/sqlite_template.git

API (HTTP request)

https://github.com/SirinK2/api_http_request.git

🦸‍♀️ SuperHero

  • Sirin Algadi.
  • Afnan Almohammdi.
  • Ahlam Albalawi.
  • Hanan Asiri.
  • Joud Almahdi.
  • Khairiah Alayyafi،
  • Razan Ajohani.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages