LCOV - code coverage report
Current view: top level - buttons - list_tile_button.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 88 93 94.6 %
Date: 2024-11-26 10:38:40 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:flutter/material.dart';
       2             : 
       3             : /// A customizable list tile button that wraps content in a rounded container
       4             : /// and provides tap and long-press callbacks. Ideal for creating interactive
       5             : /// list items with consistent styling.
       6             : ///
       7             : /// Example usage:
       8             : /// ```dart
       9             : /// ListTileButton(
      10             : ///   onPressed: () {},
      11             : ///   leading: Icon(Icons.star),
      12             : ///   body: Text('List Tile Button'),
      13             : /// );
      14             : /// ```
      15             : class ListTileButton extends StatelessWidget {
      16             :   // Behavior
      17             : 
      18             :   /// Callback when the tile is tapped.
      19             :   final VoidCallback? onPressed;
      20             : 
      21             :   /// Callback when the tile is long-pressed.
      22             :   final VoidCallback? onLongPress;
      23             : 
      24             :   // Layout
      25             : 
      26             :   /// External margin around the tile.
      27             :   final EdgeInsetsGeometry? margin;
      28             : 
      29             :   /// Internal padding within the tile.
      30             :   final EdgeInsetsGeometry? padding;
      31             : 
      32             :   /// Padding for the [body] within the [ListTile].
      33             :   final EdgeInsetsGeometry? bodyPadding;
      34             : 
      35             :   /// Padding around the [leading] widget.
      36             :   final EdgeInsetsGeometry? leadingPadding;
      37             : 
      38             :   /// Padding around the [trailing] widget.
      39             :   final EdgeInsetsGeometry? trailingPadding;
      40             : 
      41             :   // Content
      42             : 
      43             :   /// Widget to display at the start of the tile.
      44             :   final Widget? leading;
      45             : 
      46             :   /// Factor to scale the size of the leading widget.
      47             :   final double leadingSizeFactor;
      48             : 
      49             :   /// The primary content of the tile.
      50             :   final Widget? body;
      51             : 
      52             :   /// Additional content displayed below the [body].
      53             :   final Widget? subtitle;
      54             : 
      55             :   /// Widget to display at the end of the tile.
      56             :   final Widget? trailing;
      57             : 
      58             :   // Style
      59             : 
      60             :   /// Background color of the tile.
      61             :   final Color? backgroundColor;
      62             : 
      63             :   /// Border color of the tile.
      64             :   final Color? borderColor;
      65             : 
      66             :   /// Border radius of the tile's rounded corners.
      67             :   final double borderRadius;
      68             : 
      69             :   /// Elevation of the tile's shadow.
      70             :   final double? elevation;
      71             : 
      72             :   // Visual Aspects
      73             : 
      74             :   /// Visual density of the tile to control compactness.
      75             :   final VisualDensity? visualDensity;
      76             : 
      77             :   /// Alignment of the [body] within the tile.
      78             :   final ListTileTitleAlignment? bodyAlignment;
      79             : 
      80             :   // Constraints
      81             : 
      82             :   /// Minimum height of the tile.
      83             :   final double? minHeight;
      84             : 
      85             :   /// Creates a [ListTileButton] with customizable content and styling.
      86           2 :   const ListTileButton({
      87             :     super.key,
      88             :     this.onPressed,
      89             :     this.onLongPress,
      90             :     this.margin,
      91             :     this.padding,
      92             :     this.bodyPadding,
      93             :     this.leadingPadding,
      94             :     this.trailingPadding,
      95             :     this.leading,
      96             :     this.leadingSizeFactor = 1.0,
      97             :     required this.body,
      98             :     this.subtitle,
      99             :     this.trailing,
     100             :     this.backgroundColor,
     101             :     this.borderColor,
     102             :     this.borderRadius = 10,
     103             :     this.elevation,
     104             :     this.visualDensity,
     105             :     this.bodyAlignment,
     106             :     this.minHeight,
     107             :   });
     108             : 
     109           2 :   @override
     110             :   Widget build(BuildContext context) {
     111             :     Widget? leadingWidget;
     112           2 :     if (leading != null) {
     113           1 :       leadingWidget = Padding(
     114           1 :         padding: leadingPadding ?? EdgeInsets.zero,
     115           1 :         child: Center(
     116           1 :           child: SizedBox(
     117             :             key: const Key('leading_wrapper'),
     118             :             // Added Key for testing
     119           2 :             width: 24.0 * leadingSizeFactor,
     120           2 :             height: 24.0 * leadingSizeFactor,
     121           1 :             child: FittedBox(
     122             :               fit: BoxFit.contain,
     123             :               alignment: Alignment.center,
     124           1 :               child: leading,
     125             :             ),
     126             :           ),
     127             :         ),
     128             :       );
     129             :     }
     130             : 
     131             :     Widget? trailingWidget;
     132           2 :     if (trailing != null) {
     133           2 :       trailingWidget = Padding(
     134           2 :         padding: trailingPadding ?? const EdgeInsets.only(right: 12),
     135           2 :         child: Container(
     136             :           alignment: Alignment.center,
     137             :           height: double.infinity,
     138           2 :           child: trailing,
     139             :         ),
     140             :       );
     141             :     }
     142             : 
     143           2 :     return RoundedContainer(
     144           2 :       margin: margin,
     145           2 :       borderColor: borderColor,
     146           2 :       backgroundColor: backgroundColor,
     147           2 :       elevation: elevation,
     148           2 :       borderRadius: borderRadius,
     149           2 :       child: Material(
     150             :         type: MaterialType.transparency,
     151           2 :         child: InkWell(
     152           4 :           borderRadius: BorderRadius.circular(borderRadius),
     153           2 :           onTap: onPressed,
     154           2 :           onLongPress: onLongPress,
     155           2 :           child: Padding(
     156           2 :             padding: padding ?? const EdgeInsets.all(8),
     157           2 :             child: ConstrainedBox(
     158           2 :               constraints: BoxConstraints(
     159           2 :                 minHeight: minHeight ?? 50.0,
     160             :               ),
     161           2 :               child: IntrinsicHeight(
     162           2 :                 child: Row(
     163           2 :                   children: [
     164           1 :                     if (leadingWidget != null) leadingWidget,
     165           2 :                     Expanded(
     166           2 :                       child: ListTile(
     167           2 :                         titleAlignment: bodyAlignment,
     168           2 :                         visualDensity: visualDensity ?? VisualDensity.compact,
     169             :                         contentPadding:
     170           2 :                             bodyPadding ?? const EdgeInsets.only(left: 8),
     171             :                         minVerticalPadding: 0,
     172             :                         minLeadingWidth: 0,
     173           2 :                         title: body,
     174           2 :                         subtitle: subtitle,
     175             :                       ),
     176             :                     ),
     177           2 :                     if (trailingWidget != null) trailingWidget,
     178             :                   ],
     179             :                 ),
     180             :               ),
     181             :             ),
     182             :           ),
     183             :         ),
     184             :       ),
     185             :     );
     186             :   }
     187             : }
     188             : 
     189             : /// A convenience widget that combines an icon with a [ListTileButton].
     190             : ///
     191             : /// Example usage:
     192             : /// ```dart
     193             : /// IconListTileButton(
     194             : ///   icon: Icons.settings,
     195             : ///   title: Text('Settings'),
     196             : ///   onPressed: () {},
     197             : /// );
     198             : /// ```
     199             : class IconListTileButton extends StatelessWidget {
     200             :   // Behavior
     201             : 
     202             :   /// Callback when the tile is tapped.
     203             :   final VoidCallback? onPressed;
     204             : 
     205             :   // Layout
     206             : 
     207             :   /// External margin around the tile.
     208             :   final EdgeInsetsGeometry? margin;
     209             : 
     210             :   /// Internal padding within the tile.
     211             :   final EdgeInsetsGeometry? padding;
     212             : 
     213             :   /// Padding for the [body] within the [ListTile].
     214             :   final EdgeInsetsGeometry? bodyPadding;
     215             : 
     216             :   /// Padding around the [leading] widget.
     217             :   final EdgeInsetsGeometry? leadingPadding;
     218             : 
     219             :   /// Padding around the [trailing] widget.
     220             :   final EdgeInsetsGeometry? trailingPadding;
     221             : 
     222             :   // Content
     223             : 
     224             :   /// Icon to display at the start of the tile.
     225             :   final IconData icon;
     226             : 
     227             :   /// The primary content of the tile.
     228             :   final Widget title;
     229             : 
     230             :   /// Additional content displayed below the [title].
     231             :   final Widget? subtitle;
     232             : 
     233             :   /// Widget to display at the end of the tile.
     234             :   final Widget? trailing;
     235             : 
     236             :   // Style
     237             : 
     238             :   /// Background color of the tile.
     239             :   final Color? backgroundColor;
     240             : 
     241             :   /// Border color of the tile.
     242             :   final Color? borderColor;
     243             : 
     244             :   /// Color of the icon.
     245             :   final Color? iconColor;
     246             : 
     247             :   /// Factor to scale the size of the leading icon.
     248             :   final double leadingSizeFactor;
     249             : 
     250             :   /// Elevation of the tile's shadow.
     251             :   final double? elevation;
     252             : 
     253             :   /// Border radius of the tile's rounded corners.
     254             :   final double borderRadius;
     255             : 
     256             :   /// Creates an [IconListTileButton] with an icon and customizable content.
     257           1 :   const IconListTileButton({
     258             :     super.key,
     259             :     required this.icon,
     260             :     required this.title,
     261             :     this.subtitle,
     262             :     this.trailing,
     263             :     this.onPressed,
     264             :     this.backgroundColor,
     265             :     this.borderColor,
     266             :     this.iconColor,
     267             :     this.leadingSizeFactor = 1.0,
     268             :     this.margin,
     269             :     this.padding,
     270             :     this.bodyPadding,
     271             :     this.leadingPadding,
     272             :     this.trailingPadding,
     273             :     this.elevation,
     274             :     this.borderRadius = 10,
     275             :   });
     276             : 
     277           1 :   @override
     278             :   Widget build(BuildContext context) {
     279           1 :     return ListTileButton(
     280           1 :       margin: margin,
     281           1 :       padding: padding,
     282           1 :       bodyPadding: bodyPadding,
     283           1 :       leadingPadding: leadingPadding,
     284           1 :       trailingPadding: trailingPadding,
     285           1 :       backgroundColor: backgroundColor,
     286           1 :       borderColor: borderColor,
     287           1 :       elevation: elevation,
     288           1 :       borderRadius: borderRadius,
     289           1 :       body: title,
     290           1 :       subtitle: subtitle,
     291           1 :       trailing: trailing,
     292           1 :       onPressed: onPressed,
     293           1 :       leading: Icon(
     294           1 :         icon,
     295           4 :         color: iconColor ?? Theme.of(context).iconTheme.color,
     296             :         size: 24.0,
     297             :       ),
     298           1 :       leadingSizeFactor: leadingSizeFactor,
     299             :       bodyAlignment: ListTileTitleAlignment.threeLine,
     300             :     );
     301             :   }
     302             : }
     303             : 
     304             : /// A container with rounded corners and optional border and elevation.
     305             : ///
     306             : /// Used internally by [ListTileButton] to wrap content with consistent styling.
     307             : ///
     308             : /// Example usage:
     309             : /// ```dart
     310             : /// RoundedContainer(
     311             : ///   child: Text('Content'),
     312             : ///   backgroundColor: Colors.white,
     313             : ///   borderColor: Colors.grey,
     314             : /// );
     315             : /// ```
     316             : class RoundedContainer extends StatelessWidget {
     317             :   // Layout
     318             : 
     319             :   /// External margin around the container.
     320             :   final EdgeInsetsGeometry? margin;
     321             : 
     322             :   /// Internal padding within the container.
     323             :   final EdgeInsetsGeometry? padding;
     324             : 
     325             :   // Content
     326             : 
     327             :   /// The widget below this widget in the tree.
     328             :   final Widget child;
     329             : 
     330             :   // Style
     331             : 
     332             :   /// Background color of the container.
     333             :   final Color? backgroundColor;
     334             : 
     335             :   /// Border color of the container.
     336             :   final Color? borderColor;
     337             : 
     338             :   /// Border radius of the container's rounded corners.
     339             :   final double borderRadius;
     340             : 
     341             :   /// Elevation of the container's shadow.
     342             :   final double? elevation;
     343             : 
     344             :   /// Creates a [RoundedContainer] with customizable styling.
     345           2 :   const RoundedContainer({
     346             :     super.key,
     347             :     required this.child,
     348             :     this.margin,
     349             :     this.padding,
     350             :     this.backgroundColor,
     351             :     this.borderColor,
     352             :     this.borderRadius = 10,
     353             :     this.elevation,
     354             :   });
     355             : 
     356           2 :   @override
     357             :   Widget build(BuildContext context) {
     358           2 :     return Padding(
     359           2 :       padding: margin ?? EdgeInsets.zero,
     360           2 :       child: Material(
     361           2 :         elevation: elevation ?? 0,
     362           4 :         borderRadius: BorderRadius.circular(borderRadius),
     363           4 :         color: backgroundColor ?? Theme.of(context).cardColor,
     364           2 :         child: Container(
     365           2 :           padding: padding,
     366           2 :           decoration: BoxDecoration(
     367           4 :             borderRadius: BorderRadius.circular(borderRadius),
     368           2 :             border: borderColor != null
     369           0 :                 ? Border.all(color: borderColor!, width: 2)
     370             :                 : null,
     371             :           ),
     372           2 :           child: child,
     373             :         ),
     374             :       ),
     375             :     );
     376             :   }
     377             : }
     378             : 
     379             : /// A widget that displays two buttons side by side, typically used at the bottom
     380             : /// of a sheet or dialog for actions like "Confirm" and "Cancel".
     381             : ///
     382             : /// Example usage:
     383             : /// ```dart
     384             : /// DoubleListTileButtons(
     385             : ///   firstButton: ElevatedButton(
     386             : ///     onPressed: () {},
     387             : ///     child: Text('Cancel'),
     388             : ///   ),
     389             : ///   secondButton: ElevatedButton(
     390             : ///     onPressed: () {},
     391             : ///     child: Text('Confirm'),
     392             : ///   ),
     393             : /// );
     394             : /// ```
     395             : class DoubleListTileButtons extends StatelessWidget {
     396             :   /// The first button to display.
     397             :   final Widget firstButton;
     398             : 
     399             :   /// The second button to display.
     400             :   final Widget secondButton;
     401             : 
     402             :   /// Padding around the buttons.
     403             :   final EdgeInsetsGeometry padding;
     404             : 
     405             :   /// Whether the buttons should expand to fill the available width.
     406             :   final bool expanded;
     407             : 
     408             :   /// Space between the two buttons.
     409             :   final double? space;
     410             : 
     411             :   /// Creates a [DoubleListTileButtons] widget.
     412           2 :   const DoubleListTileButtons({
     413             :     super.key,
     414             :     required this.firstButton,
     415             :     required this.secondButton,
     416             :     this.padding = EdgeInsets.zero,
     417             :     this.expanded = true,
     418             :     this.space,
     419             :   });
     420             : 
     421           2 :   @override
     422             :   Widget build(BuildContext context) {
     423           2 :     return Padding(
     424           2 :       padding: padding,
     425           2 :       child: Row(
     426             :         mainAxisSize: MainAxisSize.min,
     427             :         mainAxisAlignment: MainAxisAlignment.spaceEvenly,
     428             :         crossAxisAlignment: CrossAxisAlignment.center,
     429           2 :         children: expanded
     430           2 :             ? [
     431           4 :                 Expanded(child: firstButton),
     432           4 :                 SizedBox(width: space ?? 8),
     433           4 :                 Expanded(child: secondButton),
     434             :               ]
     435           0 :             : [
     436           0 :                 firstButton,
     437           0 :                 SizedBox(width: space ?? 8),
     438           0 :                 secondButton,
     439             :               ],
     440             :       ),
     441             :     );
     442             :   }
     443             : }

Generated by: LCOV version 1.14