Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QC-15: Add 'Custom Widgets Documentation' #15

Merged
merged 1 commit into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions app/lib/global/widgets/ayat_tile/ayat_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,28 @@ import 'package:quran_companion/global/widgets/vertical_gap.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';

/// A widget that displays an Ayat (verse) with various options and translations.
///
/// The `AyatTile` widget includes the Ayat number, Arabic text, Urdu translation,
/// and options for making notes or sharing the Ayat as a PDF. It uses the `AyatTileViewModel`
/// for managing its state and handling user interactions.
class AyatTile extends StatelessWidget {
/// The Ayat (verse) number.
final int number;

/// The Arabic text of the Ayat.
final String arabic;

/// The Urdu translation of the Ayat.
final String urdu;

/// An optional word in the Urdu text to highlight.
final String? highlightedWord;

/// Creates an `AyatTile` widget with the specified properties.
///
/// The [number], [arabic], and [urdu] parameters are required.
/// The [highlightedWord] is optional and can be used to highlight specific words in the Urdu text.
const AyatTile({
super.key,
required this.number,
Expand Down Expand Up @@ -83,7 +100,7 @@ class AyatTile extends StatelessWidget {
const VerticalGap(16),
_UrduText(urdu: urdu, highlightedWord: highlightedWord),
const VerticalGap(34),
const CustomDivider(leftGap: 0, rightGap: 0),
const CustomDivider(leadingIndent: 0, trailingIndent: 0),
],
),
);
Expand All @@ -92,13 +109,18 @@ class AyatTile extends StatelessWidget {
}
}

/// A widget that displays the Ayat number with a styled background.
///
/// This widget is used to visually emphasize the Ayat number within the `AyatTile`.
class _AyatNumber extends StatelessWidget {
/// The Ayat number to display.
final int number;

/// Creates an `_AyatNumber` widget with the specified [number].
const _AyatNumber({
required this.number,
});

final int number;

@override
Widget build(BuildContext context) {
return Tooltip(
Expand All @@ -123,15 +145,23 @@ class _AyatNumber extends StatelessWidget {
}
}

/// A widget that displays the Urdu translation of the Ayat with optional highlighting.
///
/// This widget is used to render the Urdu translation in the `AyatTile`, with an
/// option to highlight a specific word.
class _UrduText extends StatelessWidget {
/// The Urdu translation of the Ayat.
final String urdu;

/// An optional word to highlight within the Urdu text.
final String? highlightedWord;

/// Creates an `_UrduText` widget with the specified [urdu] text and an optional [highlightedWord].
const _UrduText({
required this.urdu,
required this.highlightedWord,
});

final String urdu;
final String? highlightedWord;

@override
Widget build(BuildContext context) {
return SizedBox(
Expand All @@ -148,13 +178,18 @@ class _UrduText extends StatelessWidget {
}
}

/// A widget that displays the Arabic text of the Ayat.
///
/// This widget is used to render the Arabic text in the `AyatTile`, aligning it to the right.
class _ArabicText extends StatelessWidget {
/// The Arabic text of the Ayat.
final String arabic;

/// Creates an `_ArabicText` widget with the specified [arabic] text.
const _ArabicText({
required this.arabic,
});

final String arabic;

@override
Widget build(BuildContext context) {
return SizedBox(
Expand All @@ -170,10 +205,21 @@ class _ArabicText extends StatelessWidget {
}
}

/// A customizable action button used within the `AyatTile`.
///
/// This widget provides an action button with a customizable child widget,
/// tooltip, and tap callback, wrapped in a styled container.
class _CustomActionButton extends StatelessWidget {
/// The widget displayed inside the button.
final Widget child;

/// The callback function triggered when the button is tapped.
final void Function()? onTap;

/// An optional tooltip displayed when the user hovers over the button.
final String? tooltip;

/// Creates a `_CustomActionButton` widget with the specified [child], [onTap] callback, and an optional [tooltip].
const _CustomActionButton({
required this.child,
required this.onTap,
Expand All @@ -198,15 +244,19 @@ class _CustomActionButton extends StatelessWidget {
}
}

/// A custom loading indicator used within action buttons in the `AyatTile`.
///
/// This widget provides a small circular progress indicator, styled to match the app's theme.
class _CustomLoadingIndicator extends StatelessWidget {
/// Creates a `_CustomLoadingIndicator` widget.
const _CustomLoadingIndicator();

@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 13.5.w,
height: 13.5.w,
height: 13.5.h,
child: CircularProgressIndicator(
color: green,
strokeWidth: 2.w,
Expand Down
22 changes: 22 additions & 0 deletions app/lib/global/widgets/custom_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,28 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stacked_services/stacked_services.dart';

/// A customizable app bar widget with a title, subtitle, and back button.
///
/// The `CustomAppBar` widget provides a consistent app bar design featuring
/// a title, subtitle, and back button. It integrates with the `NavigationService`
/// for back navigation and allows for custom titles and subtitles. The app bar
/// also includes a drop shadow and adjusts its layout based on the device's
/// status bar height.
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
/// The navigation service used for handling back navigation.
final NavigationService _navigationService = locator<NavigationService>();

/// The primary title displayed in the app bar.
final String title;

/// The subtitle displayed below the title in the app bar.
final String subtitle;

/// Creates a `CustomAppBar` widget with a [title] and [subtitle].
///
/// The [title] and [subtitle] are required and are displayed prominently
/// in the app bar. The back button is provided by default and is linked
/// to the navigation service for back navigation.
CustomAppBar({
super.key,
required this.title,
Expand All @@ -37,6 +54,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Adds a vertical gap equal to the height of the status bar
VerticalGap(MediaQuery.of(context).viewPadding.top),
Container(
padding: EdgeInsets.only(left: 8.w),
Expand Down Expand Up @@ -77,6 +95,10 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
);
}

/// Specifies the preferred height of the app bar.
///
/// The preferred height is set to the maximum finite size, ensuring the
/// app bar adapts to the content's height.
@override
Size get preferredSize => const Size.fromHeight(double.maxFinite);
}
30 changes: 24 additions & 6 deletions app/lib/global/widgets/custom_divider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,45 @@ import 'package:quran_companion/global/themes/colors.dart';
import 'package:quran_companion/global/widgets/horizontal_gap.dart';
import 'package:flutter/material.dart';

/// A customizable horizontal divider with leading and trailing indents.
///
/// The `CustomDivider` widget creates a horizontal line (divider) with
/// configurable leading and trailing indents. The divider is styled with
/// a specific color and height, and the indents are controlled using
/// [HorizontalGap] widgets.
///
/// This widget is useful for creating visual separations in the UI, where
/// the divider does not span the full width of the container.
class CustomDivider extends StatelessWidget {
final double leftGap;
final double rightGap;
/// The amount of horizontal space before the divider starts.
final double leadingIndent;

/// The amount of horizontal space after the divider ends.
final double trailingIndent;

/// Creates a `CustomDivider` widget with specified [leadingIndent] and [trailingIndent].
///
/// The [leadingIndent] and [trailingIndent] parameters control the padding
/// on either side of the divider, allowing for precise alignment within
/// the layout.
const CustomDivider({
super.key,
required this.leftGap,
required this.rightGap,
required this.leadingIndent,
required this.trailingIndent,
});

@override
Widget build(BuildContext context) {
return Row(
children: [
HorizontalGap(leftGap),
HorizontalGap(leadingIndent),
Expanded(
child: Container(
height: 1,
color: green.withOpacity(0.2),
),
),
HorizontalGap(rightGap),
HorizontalGap(trailingIndent),
],
);
}
Expand Down
27 changes: 26 additions & 1 deletion app/lib/global/widgets/custom_elevated_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,39 @@ import 'package:quran_companion/global/themes/fonts.dart';
import 'package:quran_companion/global/widgets/custom_text.dart';
import 'package:flutter/material.dart';

/// A customizable elevated button with responsive sizing and text styling.
///
/// The `CustomElevatedButton` widget provides a button with customizable
/// dimensions, color, and label. The button's size is responsive to the
/// screen dimensions, and the label is styled using a specified font and size.
///
/// This widget is ideal for creating buttons that fit within a consistent
/// design system while allowing flexibility in appearance.
class CustomElevatedButton extends StatelessWidget {
// TODO: Change to child widget for loading indicator
/// The text label displayed on the button.
final String label;

/// The font size of the label text.
final double labelSize;

/// The callback function triggered when the button is pressed.
final void Function()? onPressed;

/// The background color of the button.
final Color color;

/// The height of the button in logical pixels.
final num height;

/// The width of the button in logical pixels.
final num width;

/// Creates a `CustomElevatedButton` widget with the specified properties.
///
/// The [label], [onPressed], [labelSize], [height], [width], and [color]
/// parameters can be customized to adjust the appearance and behavior of
/// the button. The [labelSize], [height], [width], and [color] have default
/// values, allowing you to create a button with minimal configuration.
const CustomElevatedButton({
super.key,
required this.label,
Expand Down
35 changes: 35 additions & 0 deletions app/lib/global/widgets/custom_text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,43 @@ import 'package:quran_companion/global/services/size_helper_service.dart';
import 'package:quran_companion/global/themes/colors.dart';
import 'package:flutter/material.dart';

/// A custom text widget that allows for optional highlighting of a specific word.
///
/// The `CustomText` widget provides a way to display a string of text with
/// optional highlighting of a particular word. If the [highlightedWord] is
/// present in the [data], it will be highlighted with a different background color.
/// This widget supports custom font, size, color, and text alignment.
///
/// This widget is particularly useful when you need to emphasize a part of the
/// text while maintaining a consistent style across the application.
class CustomText extends StatelessWidget {
/// The complete text string to display.
final String data;

/// The font size of the text.
final double size;

/// The font family to use for the text.
final String font;

/// The color of the text.
final Color? color;

/// An optional word to highlight within the text.
///
/// If [highlightedWord] is present in the [data], it will be highlighted
/// with a different background color.
final String? highlightedWord;

/// The alignment of the text.
///
/// Defaults to [TextAlign.left].
final TextAlign textAlign;

/// Creates a `CustomText` widget.
///
/// The [data], [size], and [font] are required. The [color] and [highlightedWord]
/// are optional. The [textAlign] defaults to [TextAlign.left].
const CustomText(
this.data, {
super.key,
Expand All @@ -22,6 +51,8 @@ class CustomText extends StatelessWidget {

@override
Widget build(BuildContext context) {
// If there is no highlighted word or the highlighted word is not found,
// return the text as a simple Text widget.
if (highlightedWord == null || !data.contains(highlightedWord!)) {
return Text(
data,
Expand All @@ -34,9 +65,11 @@ class CustomText extends StatelessWidget {
);
}

// Split the text into parts based on the highlighted word.
final parts = data.split(highlightedWord!);
final spans = <TextSpan>[];

// Build a list of TextSpans, with the highlighted word styled differently.
for (int i = 0; i < parts.length; i++) {
spans.add(
TextSpan(
Expand All @@ -49,6 +82,7 @@ class CustomText extends StatelessWidget {
),
);

// Add the highlighted word between the parts, except after the last part.
if (i < parts.length - 1) {
spans.add(
TextSpan(
Expand All @@ -64,6 +98,7 @@ class CustomText extends StatelessWidget {
}
}

// Return the text as a RichText widget with the appropriate spans.
return RichText(
text: TextSpan(children: spans),
textAlign: textAlign,
Expand Down
15 changes: 15 additions & 0 deletions app/lib/global/widgets/horizontal_gap.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
import 'package:flutter/material.dart';
import 'package:quran_companion/global/services/size_helper_service.dart';

/// A widget that provides horizontal spacing between other widgets.
///
/// The `HorizontalGap` widget is a simple wrapper around [SizedBox]
/// that adds horizontal space based on the given [width]. The width
/// is provided in logical pixels and converted to the appropriate
/// responsive width using the `.w` extension.
///
/// This widget is useful for adding consistent horizontal spacing
/// in layouts, especially when using responsive design principles.
class HorizontalGap extends StatelessWidget {
/// The width of the gap in logical pixels.
final double width;

/// Creates a `HorizontalGap` widget with the specified [width].
///
/// The [width] parameter defines the horizontal space this widget
/// will occupy in logical pixels.
const HorizontalGap(this.width, {super.key});

@override
Expand Down
Loading
Loading