PiPouch

pub package License: MIT code style: flutter analyze

A comprehensive collection of essential Flutter helpers, extensions, functions, and reusable widgets designed to accelerate your app development and promote cleaner, more efficient code. From robust API response handling to versatile data formatting and UI enhancements, PiPouch equips you with ready-to-use solutions.

✨ Why PiPouch?

Developing Flutter apps often involves writing repetitive boilerplate code, handling common tasks, or creating similar UI components. PiPouch aims to streamline your workflow by providing a curated set of tested and ready-to-use utilities, allowing you to focus on your app's unique features rather than reinventing the wheel.

With PiPouch, you get:

  • ⚡️ Increased Productivity: Reduce development time by leveraging pre-built solutions for common scenarios.
  • 🧼 Cleaner Codebase: Utilize powerful extensions for String, num, DateTime, File, Color, Duration, and Iterable to write more concise and readable code.
  • 🌐 Robust API Handling: Simplify network response checks and error localization with dedicated extensions.
  • 🎨 Flexible UI Enhancements: Apply common styles and layouts to any widget with handy Widget extensions.
  • ⚙️ Common Utilities: A collection of functions to handle everyday tasks efficiently.

🚀 Getting Started

1. Add PiPouch to your pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  pipouch: ^1.0.0 # Use the latest version from pub.dev
  # If you are using Dio for networking, ensure it's also in your dependencies:
  dio: ^5.0.0 # Or your current Dio version
  shimmer: ^3.0.0 # Required for PiButton's shimmer loading type

Then, run flutter pub get in your terminal.

2. Import PiPouch

import 'package:pipouch/pipouch.dart';
// You might also need specific imports depending on what you use:
// import 'package:pipouch/utils/localization/pi_abs_localization.dart'; // If you're extending localization
// import 'package:pipouch/utils/localization/pi_dio_error_localization.dart'; // For specific error localization

📖 Usage Examples

Here are some quick examples to get you started with PiPouch's powerful utilities.

API Response Handling (Response Extension)

Simplify checking API response statuses and getting localized messages. Requires dio package.

import 'package:dio/dio.dart';
import 'package:pipouch/pipouch.dart'; // Make sure to import

Future<void> handleResponse(Response response) async {
  if (response.isSuccess) {
    print('Request was successful!');
    print('Message: ${response.statusCodeMessage()}'); // Default locale 'id_ID'
  } else if (response.isTokenExpired) {
    print('Token Expired! Please re-authenticate.');
    print('Message: ${response.statusCodeMessage('en_US')}'); // Specify locale
  } else if (response.isNotFound) {
    print('Resource not found!');
  } else {
    print('An error occurred: ${response.statusCodeMessage()}');
  }
}

// Example usage with Dio
// try {
//   final dio = Dio();
//   final response = await dio.get('[https://api.example.com/data](https://api.example.com/data)');
//   handleResponse(response);
// } on DioException catch (e) {
//   if (e.response != null) {
//     handleResponse(e.response!);
//   } else {
//     print('Network Error: ${e.message}');
//   }
// }

Data Type Extensions

num Extensions

Format numbers into currency or short forms.

import 'package:pipouch/pipouch.dart';

print(100000.toShortK); // Output: 100K
print(75500.toShortK);  // Output: 75K

print(100000.toRupiah); // Output: Rp 100.000
print(75500.toRupiahNoSymbol); // Output: 75.000

String Extensions

Extensive utilities for string manipulation, conversion, and validation.

import 'package:pipouch/pipouch.dart';
import 'dart:ui'; // For Locale

// Null or Empty check
String? text = null;
print(text.isEmptyOrNull); // Output: true
text = '';
print(text.isEmptyOrNull); // Output: true
print('Hello'.isEmptyOrNull); // Output: false
print(''.toEmptyNullReplacement(replacement: 'N/A')); // Output: N/A

// Capitalization
print('hello world'.toCapitalize()); // Output: Hello world
print('this is a title'.toTitleCase()); // Output: This Is A Title
print('another example'.extCapitalizeFirstLetter()); // Output: Another example
print('final check'.toCapitalizeTitle()); // Output: Final Check

// Phone number formatting
print('08123456789'.toCCPhoneNumber()); // Output: 628123456789 (default 'id' locale)
print('08123456789'.toCCPhoneNumber(locale: const Locale('en'))); // Output: 18123456789

// Type conversion
print('123'.toInt); // Output: 123
print('123.45'.toDouble); // Output: 123.45
print('true'.toBoolean); // Output: true

// Date/Time conversion & formatting
final String dateString = '2023-01-20';
final DateTime dateTime = dateString.toDateTime(); // Output: 2023-01-20 00:00:00.000
print(dateString.toFormattedDate(outputDateFormat: 'dd MMMMître')); // Output: 20 Januari 2023
print('2023-01-20 15:30:00'.toDateLocale()); // Output: 20-01-2023 - 15:30

// String utilities
print('testāḥ'.toReplaceSpecialChar()); // Output: testah
print('L0RD'.replaceLetterToNumber); // Output: L0RD (example showing how letters are replaced)
print('01245678'.replaceNumberToLetter); // Output: OIZASGTB
print('John Doe'.toInitialWord()); // Output: JD

// File type checks
print('image.jpg'.isImage); // Output: true
print('animation.json'.isLottie); // Output: true

DateTime Extensions

Format dates and calculate time differences.

import 'package:pipouch/pipouch.dart';
import 'dart:ui'; // For Locale

final DateTime myDate = DateTime(2023, 1, 15, 10, 30);
print(myDate.toFormattedString(outputDateFormat: 'dd/MM/yyyy')); // Output: 15/01/2023
print(myDate.toTimeString()); // Output: 10:30
print(myDate.toDateStringDDMMMMYYYY()); // Output: 15 Januari 2023

// Time Ago (requires localization setup)
final DateTime fiveMinutesAgo = DateTime.now().subtract(const Duration(minutes: 5));
print(fiveMinutesAgo.toTimeAgo(locale: const Locale('en'))); // Output: 5 minutes ago
final DateTime twoDaysAgo = DateTime.now().subtract(const Duration(days: 2));
print(twoDaysAgo.toTimeAgo(locale: const Locale('id'))); // Output: 2 hari lalu

File Extensions

Get file properties like size and Base64 encoding.

import 'dart:io';
import 'package:pipouch/pipouch.dart';

// Example: (Assuming 'my_file.txt' exists and is accessible)
// final File myFile = File('path/to/my_file.txt');
// print('Size in KB: ${myFile.toSizeInKB} KB');
// print('Size in MB: ${myFile.toSizeInMB} MB');
// print('Base64: ${myFile.toBase64?.substring(0, 30)}...'); // Print first 30 chars

Color Extensions

Determine if a color is light or dark based on its greyscale value.

import 'package:flutter/material.dart';
import 'package:pipouch/pipouch.dart';

const Color darkColor = Colors.black;
const Color lightColor = Colors.white;
const Color midColor = Colors.blue;

print(darkColor.isDark);  // Output: true
print(lightColor.isLight); // Output: true
print(midColor.isDark);   // Output: (depends on blue shade, likely false)

Duration Extensions

Format durations into human-readable remaining time.

import 'package:flutter/material.dart';
import 'package:pipouch/pipouch.dart';

const Duration twentyMinutes = Duration(minutes: 20);
print(twentyMinutes.toRemaining(const Locale('en'))); // Output: 20 minutes

const Duration threeHoursTenMinutes = Duration(hours: 3, minutes: 10);
print(threeHoursTenMinutes.toRemaining(const Locale('id'))); // Output: 3 jam 10 menit

const Duration oneSecond = Duration(seconds: 1);
print(oneSecond.toRemaining(const Locale('en'))); // Output: 1 second (if no hours/minutes)

Iterable Extension

Adds mapIndexed method, useful for mapping with an index.

import 'package:pipouch/pipouch.dart';

final List<String> items = ['apple', 'banana', 'cherry'];
final List<String> indexedItems = items.mapIndexed((item, index) {
  return '$index: $item';
}).toList();
print(indexedItems); // Output: [0: apple, 1: banana, 2: cherry]

Widget Extensions (Widget Extension)

Apply common visual effects and layouts concisely to any widget.

import 'package:flutter/material.dart';
import 'package:pipouch/pipouch.dart';

// Example: Apply margin, border radius, and shimmer effect
Widget myContent = Container(
  width: 100,
  height: 100,
  color: Colors.grey[300],
);

Widget styledWidget = myContent
    .shimmer() // Add shimmer effect
    .borderRadius(all: 12.0) // Apply border radius to all corners
    .margin(all: 16.0) // Apply margin on all sides
    .backgroundColor(color: Colors.lightBlue); // Add a background color

// To use in your build method:
// Scaffold(
//   body: Center(
//     child: styledWidget,
//   ),
// );

// Example: inner shadow
Widget shadowContainer = Container(
  width: 150,
  height: 50,
  color: Colors.white,
  child: const Center(child: Text('With Inner Shadow')),
).innerShadow(shadowColor: Colors.black.withOpacity(0.1)); // Apply inner shadow

// Example: Semantics for accessibility
Widget accessibleText = const Text('Click me').semantics('my_button_text'); // Add semantics

Custom Widgets

PiButton

A highly customizable button widget with various styles, shapes, sizes, and states. It supports different types (filled, outlined, label), loading indicators (circular, shimmer), and a wide range of visual customizations.

Parameters:

  • onTap: void Function()? - Callback when the button is tapped.
  • onDoubleTap: void Function()? - Callback for double-tap events.
  • onLongPress: void Function()? - Callback for long-press events.
  • label: String? - The text displayed on the button.
  • iconPreffix: Widget? - An optional icon before the label.
  • iconSuffix: Widget? - An optional icon after the label.
  • shape: PiButtonShape - Defines the button's shape (e.g., SQUARE, ROUNDED, CIRCLE).
  • color: PiButtonColor - Defines the semantic color of the button (e.g., PRIMARY, DANGER, SUCCESS).
  • size: PiButtonSize - Defines the button's size (e.g., SMALL, MEDIUM, LARGE).
  • state: PiButtonState - Defines the button's interactive state (e.g., IDLE, DISABLE, LOADING).
  • layout: PiButtonLayout - Defines how the button fills space (WRAP or FULL).
  • type: PiButtonType - Defines the button's visual style (e.g., FILLED, OUTLINED, LABEL).
  • loadingType: PiButtonLoadingType - Specifies the visual style of the loading indicator (CIRCULAR, SHIMMER).
  • Customization (Optional): textStyle, backgroundColor, foregroundColor, borderRadius, horizontalPadding, verticalPadding, loadingIconColor, loadingIconStrokeWidth, loadingRadius, iconPreffixColor, iconSuffixColor, splashColor, height, width, borderWidth.

Enums:

  • PiButtonShape: SQUARE, ROUNDED, ROUNDED_FULL, CIRCLE.
  • PiButtonColor: PRIMARY, SECONDARY, SUCCESS, WARNING, INFO, DANGER, DISABLE.
  • PiButtonSize: SMALL, MEDIUM, LARGE.
  • PiButtonState: IDLE, SUCCESS, DISABLE, LOADING.
  • PiButtonLayout: WRAP, FULL.
  • PiButtonType: FILLED, OUTLINED, LABEL.
  • PiButtonLoadingType: CIRCULAR, SHIMMER.

Usage Examples:

import 'package:flutter/material.dart';
import 'package:pipouch/pipouch.dart';

// ... inside a StatelessWidget or StatefulWidget's build method
Column(
  spacing: 8, // Assuming Column.spacing is available or replaced with SizedBox
  mainAxisAlignment: MainAxisAlignment.center,
  children: <Widget>[
    const Text("Button By Size"),
    PiButton(onTap: () {}, label: "Login", size: PiButtonSize.LARGE),
    PiButton(onTap: () {}, label: "Login", size: PiButtonSize.MEDIUM),
    PiButton(onTap: () {}, label: "Login", size: PiButtonSize.SMALL),

    const Text("Button By Size & Shape"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      shape: PiButtonShape.CIRCLE,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.SMALL,
      shape: PiButtonShape.ROUNDED_FULL,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      shape: PiButtonShape.ROUNDED,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      shape: PiButtonShape.SQUARE,
    ),

    const Text("Button By Size & Layout"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      layout: PiButtonLayout.FULL, // Occupies full width
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.SMALL,
      layout: PiButtonLayout.WRAP, // Wraps content
    ),

    const Text("Button By Size & State"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      state: PiButtonState.LOADING, // Shows loading indicator
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      state: PiButtonState.DISABLE, // Button is disabled
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.SMALL,
      state: PiButtonState.IDLE, // Normal state
    ),

    const Text("Button By Size & Loading Type"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      state: PiButtonState.LOADING,
      loadingType: PiButtonLoadingType.CIRCULAR, // Circular progress indicator
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      state: PiButtonState.LOADING,
      loadingType: PiButtonLoadingType.SHIMMER, // Shimmer effect
    ),

    const Text("Button By Size & Type"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      type: PiButtonType.FILLED, // Solid background
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.OUTLINED, // Bordered
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.LABEL, // Text-only, no background/border
    ),

    const Text("Button By Size, Type & Color"),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      type: PiButtonType.FILLED,
      color: PiButtonColor.PRIMARY,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.OUTLINED,
      color: PiButtonColor.SECONDARY,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.LABEL,
      color: PiButtonColor.SUCCESS,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      type: PiButtonType.FILLED,
      color: PiButtonColor.INFO,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.OUTLINED,
      color: PiButtonColor.DANGER,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.MEDIUM,
      type: PiButtonType.LABEL,
      color: PiButtonColor.WARNING,
    ),
    PiButton(
      onTap: () {},
      label: "Login",
      size: PiButtonSize.LARGE,
      type: PiButtonType.FILLED,
      color: PiButtonColor.DISABLE,
    ),
  ],
)

🤝 Contributing

We welcome contributions! If you have a useful helper, extension, function, or widget that fits the scope of PiPouch, feel free to:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feature/your-feature-name).
  3. Make your changes and write tests.
  4. Commit your changes (git commit -m 'feat: Add amazing new feature').
  5. Push to the branch (git push origin feature/your-feature-name).
  6. Open a Pull Request.

Please ensure your code adheres to Flutter's Effective Dart guidelines and includes appropriate documentation comments.

🐛 Issues and Feedback

Encountered a bug? Have a feature request? Please open an issue on our GitHub Issue Tracker.

📜 License

PiPouch is released under the MIT License.


Copyright © 2025 Rafi Fitra Alamsyah/PiPo. All rights reserved.

Libraries

components/button/enums/pi_button_color_enum
V Button Type Enum This file defines the PiButtonType enum which specifies different types for the PiButton widget.
components/button/enums/pi_button_layout_enum
V Button Layout Enum This file defines the PiButtonLayout enum which specifies the layout behavior of the PiButton widget (e.g., hug content size or fill available space).
components/button/enums/pi_button_loading_type_enum
V Button Loading Type Enum This file defines the PiButtonLoadingType enum which specifies the types of loading indicators that can be displayed on the PiButton widget.
components/button/enums/pi_button_shape_enum
V Button Shape Enum This file defines the PiButtonShape enum which specifies the shape of the PiButton widget.
components/button/enums/pi_button_size_enum
V Button Size Enum This file defines the PiButtonSize enum which specifies different sizes for the PiButton widget.
components/button/enums/pi_button_state_enum
components/button/enums/pi_button_type_enum
V Button Border Enum This file defines the PiButtonBorder enum which specifies different border styles for the PiButton widget.
components/button/pi_button
pipouch
themes/buttons/pi_button_color_theme
themes/buttons/pi_button_font_size_theme
themes/buttons/pi_button_icon_size_theme
themes/buttons/pi_button_padding_theme
themes/buttons/pi_button_shape_theme
themes/buttons/pi_button_size
themes/buttons/pi_button_size_theme
themes/buttons/pi_button_theme_data
themes/buttons/pi_button_type_theme
themes/pi_color_data
themes/pi_theme
themes/pi_theme_data
utils/extensions/pi_color_extension
utils/extensions/pi_date_time_extension
utils/extensions/pi_duration_extension
utils/extensions/pi_file_extension
utils/extensions/pi_iteration_extension
utils/extensions/pi_number_extension
utils/extensions/pi_response_extension
utils/extensions/pi_string_extension
utils/extensions/pi_widget_extension
utils/localization/pi_abs_localization
utils/localization/pi_dio_error_localization
utils/localization/pi_locale_dictionary
utils/localization/pi_locale_label