flexa 0.1.5
flexa: ^0.1.5 copied to clipboard
Flexa is a robust utility for handling responsive UI scaling in Flutter applications
Flexa: Effortless Responsive Design for Flutter #
Flexa is a powerful and intuitive Flutter package designed to simplify the creation of responsive user interfaces across various screen sizes, densities, and orientations. By providing intelligent scaling based on design breakpoints and device metrics, Flexa helps you build beautiful and consistent UIs on phones, tablets, and even desktops.
Stop the boilerplate of manually calculating sizes based on MediaQuery. With Flexa, you define your design once, and Flexa handles the scaling for you, ensuring your app looks great on any device. Flexa requires you to wrap your application in FlexaScope to ensure proper initialization and access to device metrics.
Features #
- Required Initialization: Explicitly requires wrapping your app in
FlexaScopefor safe and correct initialization based onBuildContext. - Breakpoint-Based Scaling: Define base sizes for different device categories (phone, tablet, large, xLarge, xxLarge) and let Flexa scale your UI elements intelligently using ratios against these base sizes.
- Smart Scaling: Provides an intelligent scaling approach that adapts based on predefined device category breakpoints, offering more nuanced control than simple ratio scaling.
- Adaptive Scaling: A balanced scaling approach that interpolates between original size and width-scaled size for harmonious layouts, particularly useful for padding and spacing.
- Font Scaling: Seamlessly scales font sizes using the adaptive scale while optionally respecting the user's system text scaling preferences.
- Physical Unit Conversion: Convert physical units like centimeters, millimeters, and inches to pixels based on device PPI, ensuring consistent physical dimensions across devices.
- Percentage-Based Sizing: Easily define widths and heights as a percentage of the screen dimensions.
- Comprehensive Breakpoint Detection: Provides getters to check if the current screen width falls within specific exclusive ranges (phone, tablet, large, xLarge) or is larger than a certain breakpoint (phone, tablet, large, xLarge, xxLarge).
- Orientation Awareness: Provides getters to check the current screen dimensions' orientation (portrait or landscape). Automatically adjusts the effective base size used for ratio scaling based on this orientation detected during initialization.
- Convenient Extensions: Use handy extensions on
num(e.g.,16.ws,10.font,50.w,20.adaptive) for concise and readable scaling directly on numerical values. FlexaScopeWidget: A dedicated widget to simplify the initialization of Flexa within your widget tree, typically near the root.whenMethod: A flexible utility for building conditional layouts or providing values based on predefined screen width breakpoints (phone, tablet, large, xLarge, xxLarge).- Parent-Based Sizing: Extensions (
.pw,.ph) to create widgets whose dimensions are calculated as a percentage of their parent's constraints. - Spacer Widgets: Quick getters (
.verticalSpace,.horizontalSpace,.box) to createSizedBoxwidgets with dimensions scaled adaptively.
Installation #
Add the following dependency to your pubspec.yaml file:
dependencies:
flexa: <latest_version>
Then, run flutter pub get to install the package.
🏗️ Usage Example #
SizedBox(height: 20.adaptive); // Adaptive scaling
SizedBox(width: 10.w); // 10% of screen width
SizedBox(height: 5.h); // 5% of screen height
Text('Hello', style: TextStyle(fontSize: 16.font)); // Adaptive text
FlexaScope #
A convenient StatefulWidget for initializing Flexa automatically whenever its dependencies (like screen size or text scale factor from MediaQuery) change. Wrap your root app widget (MaterialApp or CupertinoApp) with FlexaScope.
FlexaScope({required this.builder, this.systemFontScale, this.baseSize, super.key}): Constructor requires abuilderfunction that returns the widget tree to be wrapped.systemFontScaleandbaseSizeare optional overrides forFlexa.init.
FlexaUnits Extension on num #
Provides convenient getter properties and methods on any number (int, double) to easily apply Flexa scaling. Available after FlexaScope has initialized Flexa.
double get ws: AppliesFlexa.instance.widthScale(toDouble()).double get wss: AppliesFlexa.instance.smartWidthScale(toDouble()).double get hs: AppliesFlexa.instance.heightScale(toDouble()).double get hss: AppliesFlexa.instance.smartHeightScale(toDouble()).double get adaptive: AppliesFlexa.instance.adaptiveScale(toDouble()). Use for padding, margins, gaps, etc.double get font: AppliesFlexa.instance.fontScale(toDouble()). Use for font sizes.double get w: AppliesFlexa.instance.widthPercent(toDouble()). Use for widths relative to screen width.double get h: AppliesFlexa.instance.heightPercent(toDouble()). Use for heights relative to screen height.double get cm: AppliesFlexa.instance.cm(toDouble()). Converts cm to pixels.double get mm: AppliesFlexa.instance.mm(toDouble()). Converts mm to pixels.double get inch: AppliesFlexa.instance.inches(toDouble()). Converts inches to pixels.double get physical: AppliesFlexa.instance.physical(toDouble()). Converts a logical size to a physical pixel size.SizedBox get verticalSpace: Returns aSizedBoxwithheight: this.adaptive.SizedBox get horizontalSpace: Returns aSizedBoxwithwidth: this.adaptive.SizedBox get box: Returns aSizedBoxwithwidth: this.adaptiveandheight: this.adaptive.Widget pw(BuildContext context, Widget Function(double width) builder): Returns aLayoutBuilderthat calculates a width asthispercentage of the parent'smaxWidthand passes the calculated width to thebuilderfunction.Widget ph(BuildContext context, Widget Function(double height) builder): Returns aLayoutBuilderthat calculates a height asthispercentage of the parent'smaxHeightand passes the calculated height to thebuilderfunction.
The Power of .adaptive Scaling #
While .ws and .hs provide proportional scaling based on your base design size, and .wss/.hss offer intelligent scaling based on breakpoints, the .adaptive getter provides a balanced approach ideal for padding, spacing, gaps, and small, consistent UI elements.
Adaptive scaling interpolates between the original size and a width-scaled size (specifically, the smartWidthScale is used internally for the base of this calculation) using a defined factor (defaulting to 0.5). This means it doesn't scale as aggressively as pure width/height scaling, resulting in more harmonious and less extreme size changes for elements that shouldn't fully stretch or shrink with the screen dimensions.
Why use .adaptive?
- Harmonious Spacing: Ensures padding, margins, and the space between widgets feel visually balanced across different screen sizes without becoming excessively large or tiny.
- Consistent Element Size: Suitable for small icons, fixed-size buttons, or other elements where maintaining a somewhat consistent, yet responsive, physical appearance is desired, rather than strictly scaling with width or height ratios.
- Reduces Extreme Scaling: By interpolating, it mitigates the chance of sizes becoming disproportionately large on very big screens or too small on very small ones, which can sometimes happen with simple ratio scaling (
.ws,.hs).
Best Used For:
EdgeInsets(padding, margin)SizedBox(creating gaps)Spacer(inRoworColumn)- Border widths
- Icon sizes (sometimes, depending on design needs)
- Constraints for smaller widgets
Example:
Using .adaptive for padding and spacing creates a rhythm that adjusts comfortably across devices:
import 'package:flutter/material.dart';
import 'package:flexa/flexa.dart';
class AdaptiveExample extends StatelessWidget {
const AdaptiveExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Adaptive Scale Example')),
body: Center(
child: Container(
// Use .adaptive for symmetrical padding
padding: EdgeInsets.symmetric(vertical: 24.adaptive, horizontal: 16.adaptive),
decoration: BoxDecoration(
color: Colors.blue[50],
borderRadius: BorderRadius.circular(8.adaptive), // Adaptive border radius
border: Border.all(width: 2.adaptive, color: Colors.blueAccent), // Adaptive border width
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Adaptive Spacing and Padding',
style: TextStyle(fontSize: 18.font), // Use .font for text
),
// Use .verticalSpace and .horizontalSpace (which use .adaptive internally)
16.verticalSpace,
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, size: 30.adaptive, color: Colors.amber), // Adaptive icon size
12.horizontalSpace,
Text('Some Content', style: TextStyle(fontSize: 16.font)),
12.horizontalSpace,
Icon(Icons.settings, size: 30.adaptive, color: Colors.grey[700]), // Adaptive icon size
],
),
16.verticalSpace,
Container(
width: 150.ws, // Use .ws for content width
height: 80.hs, // Use .hs for content height
color: Colors.blueAccent.withOpacity(0.3),
child: Center(child: Text('Scaled Content', style: TextStyle(fontSize: 14.font))),
),
],
),
),
),
);
}
}
When Expression #
Here is an example demonstrating the usage of the Flexa.when method to build responsive layouts or provide breakpoint-specific values:
import 'package:flutter/material.dart';
import 'package:flexa/flexa.dart'; // Assuming your package is named flexa
class ResponsiveTextExample extends StatelessWidget {
const ResponsiveTextExample({super.key});
@override
Widget build(BuildContext context) {
// Use Flexa.when to return a different Text widget
// based on the current screen width breakpoints.
final responsiveWidget = Flexa.when(
phone: () => Text(
'Viewing on a Phone',
style: TextStyle(fontSize: 16.font, color: Colors.blue),
),
tablet: () => Text(
'Viewing on a Tablet',
style: TextStyle(fontSize: 20.font, color: Colors.green),
),
large: () => Text(
'Viewing on a Large Screen',
style: TextStyle(fontSize: 24.font, color: Colors.orange),
),
xLarge: () => Text(
'Viewing on an xLarge Screen',
style: TextStyle(fontSize: 28.font, color: Colors.red),
),
xxLarge: () => Text(
'Viewing on an xxLarge Screen',
style: TextStyle(fontSize: 32.font, color: Colors.purple),
),
// Note: You must provide at least one breakpoint handler.
// Flexa.when falls back to the smallest provided if no specific match.
// If phone() is provided, it acts as the default for screens below 600.
);
// You can also use Flexa.when to return different values, not just widgets.
// For example, determining the number of columns in a grid.
final int columns = Flexa.when(
phone: () => 1,
tablet: () => 2,
large: () => 3,
xLarge: () => 4,
xxLarge: () => 5,
// Since all breakpoints are covered, no explicit fallback is strictly needed,
// but 'phone: () => 1' acts as the default for screens < 600.
);
// Example of returning a different size or constant based on breakpoint
final double containerHeight = Flexa.when(
phone: () => 50.0.hs, // Scaled height for phone
tablet: () => 100.0, // Fixed height for tablet
large: () => 150.0.hs, // Scaled height for large
// If no specific handler for xLarge/xxLarge, it will fall back to 'large' (150.0.hs)
// based on the `when` implementation's descending check and fallback logic.
);
return Scaffold(
appBar: AppBar(
title: const Text('Flexa.when Example'),
),
body: Center(
child: SingleChildScrollView( // Added for small screens to prevent overflow
padding: EdgeInsets.symmetric(vertical: 20.adaptive),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Widget based on breakpoint:',
style: TextStyle(fontSize: 16.font),
textAlign: TextAlign.center,
),
SizedBox(height: 10.adaptive),
// Display the widget returned by Flexa.when
responsiveWidget,
SizedBox(height: 30.adaptive),
Text(
'Value based on breakpoint:',
style: TextStyle(fontSize: 16.font),
textAlign: TextAlign.center,
),
SizedBox(height: 10.adaptive),
Text(
'Number of columns: $columns',
style: TextStyle(fontSize: 16.font),
textAlign: TextAlign.center,
),
SizedBox(height: 30.adaptive),
Text(
'Size based on breakpoint:',
style: TextStyle(fontSize: 16.font),
textAlign: TextAlign.center,
),
SizedBox(height: 10.adaptive),
Container(
height: containerHeight,
width: 200.ws, // Use another Flexa scale for width
color: Colors.blueGrey,
child: Center(
child: Text(
'Container with breakpoint-dependent height',
style: TextStyle(fontSize: 16.font, color: Colors.white),
textAlign: TextAlign.center,
),
),
)
],
),
),
),
);
}
}
Reference #
The core class managing scaling logic and device information. Access its static members. The instance getter will throw an exception if Flexa.init has not been called (i.e., if FlexaScope is not used).
static Flexa get instance: Provides access to the singleton instance ofFlexa. Throws an exception if Flexa has not been initialized viaFlexaScope.static bool get systemFontScale: Returnstrueif system font scaling is currently enabled and applied by Flexa.static double get screenHeight: Returns the current screen height in logical pixels.static double get screenWidth: Returns the current screen width in logical pixels.static bool get isPhone: Returnstrueif the screen width is >= 360 AND < 600 logical pixels.static bool get isPhoneOrLarger: Returnstrueif the screen width is >= 360 logical pixels.static bool get isTablet: Returnstrueif the screen width is >= 600 AND < 1024 logical pixels.static bool get isTabletOrLarger: Returnstrueif the screen width is >= 600 logical pixels.static bool get isLarge: Returnstrueif the screen width is >= 1024 AND < 1280 logical pixels.static bool get isLargeOrLarger: Returnstrueif the screen width is >= 1024 logical pixels.static bool get isxLarge: Returnstrueif the screen width is >= 1280 AND < 1920 logical pixels.static bool get isxLargeOrLarger: Returnstrueif the screen width is >= 1280 logical pixels.static bool get isxxLargeOrLarger: Returnstrueif the screen width is >= 1920 logical pixels.static bool get isScreenDimensionsPortrait: Returnstrueif the screen height is >= the screen width.static bool get isScreenDimensionsLandscape: Returnstrueif the screen width is > the screen height.static void init(BuildContext context, {bool? systemFontScale, Size? baseSize}): Initializes theFlexainstance. Called automatically byFlexaScope.contextis required.systemFontScaleoverrides the default detection.baseSizeoverrides the default breakpoint-based base size.static T when<T>({T Function()? phone, T Function()? tablet, T Function()? large, T Function()? xLarge, T Function()? xxLarge}): A utility method to return a value based on the current screen width hitting predefined breakpoints. It returns the value for the smallest matching breakpoint. At least one size-specific function must be provided.double heightScale(double size): Scales a heightsizebased on the ratio of current screen height to the base height, clamped between 0.5 and 3.0.double smartHeightScale(double size, {double minClamp = 0.5, double maxClamp = 3.0}): Scales a heightsizeintelligently based on a reference height determined by device breakpoints (Flexa.when), clamped betweenminClampandmaxClamp.double widthScale(double size): Scales a widthsizebased on the ratio of current screen width to the base width, clamped between 0.5 and 3.0.double smartWidthScale(double size, {double minClamp = 0.5, double maxClamp = 3.0}): Scales a widthsizeintelligently based on a reference width determined by device breakpoints (Flexa.when), clamped betweenminClampandmaxClamp.double adaptiveScale(double size): Returns a balanced scaled value by interpolating between the originalsizeand its smart width scaled value using_adaptiveFactor. Good for padding, spacing, and gap sizes.double fontScale(double size): Scales a fontsizeusing the adaptive scale, then applies the system text scale factor if enabled.double physical(double size): Converts a logicalsizeto pixels to represent the same physical dimension across devices based on PPI. Useful for elements that should have a consistent physical size regardless of screen density.double cm(double size): Converts a size in centimeters to pixels using the device's PPI.double mm(double size): Converts a size in millimeters to pixels using the device's PPI.double inches(double size): Converts a size in inches to pixels using the device's PPI.double widthPercent(double percent): Calculates a width that ispercentof the total screen width.double heightPercent(double percent): Calculates a height that ispercentof the total screen height.
🛠️ Contributing #
Contributions are welcome! If you find bugs, improvements, or need new features, feel free to submit an issue or pull request.
📜 License #
This project is licensed under the MIT License. Feel free to use and modify it as needed.