A comprehensive Flutter package for responsive UI design and network connectivity management.
A lightweight, intuitive state management solution for Flutter applications.
Table of Contents
Features
📱 Responsive UI : Easily create responsive layouts that adapt to different screen sizes and orientations
📦 Custom Sized Box : Convenient extensions for creating SizedBox widgets
🌐 Internet Connectivity : Built-in connectivity monitoring with customizable no-internet UI
💱 Currency Converter : Live exchange rates and currency conversion utilities
📦 Package Management : Manage dependencies with ease
Installation
dependencies:
fittor: ^latest_version
flutter pub add fittor
Then run:
flutter pub get
Usage
Responsive
Fittor provides a responsive design system through mixins and extensions. Here's how to use it:
Basic Setup
Add the FittorAppMixin to your app:
class MyApp extends StatelessWidget with FittorAppMixin {
const MyApp ({super .key});
@override
Widget responsive (BuildContext context) {
return MaterialApp (
title: 'Responsive Demo' ,
theme: ThemeData (primarySwatch: Colors .blue),
home: const HomeScreen (),
);
}
}
Using in Widgets
Access responsive values through context extensions:
Container (
width: context.wp (50 ),
height: context.hp (25 ),
padding: EdgeInsets .all (context.p16),
child: Text (
'Responsive Text' ,
style: TextStyle (fontSize: context.fs18),
),
)
Responsive Mixins
Use the FittorMixin in your StatefulWidget:
class _MyWidgetState extends State <MyWidget > with FittorMixin {
@override
Widget build (BuildContext context) {
return Container (
width: wp (50 ),
padding: EdgeInsets .all (p16),
child: Text (
'Hello World' ,
style: TextStyle (fontSize: fs (18 )),
),
);
}
}
Custom Sized Box
Create SizedBox widgets with simple extensions:
20 .w
16 .h
24 .s
Internet Connectivity
Fittor includes built-in internet connectivity monitoring without any external packages.
Using ConnectivityWrapper
class MyApp extends StatelessWidget with FittorAppMixin {
const MyApp ({super .key});
@override
Widget responsive (BuildContext context) {
return MaterialApp (
home: ConnectivityWrapper (
ignoreOfflineState: true ,
onConnectivityChanged: (status) {
debugPrint ('Connectivity status: $status ' );
},
child: const HomeScreen (),
),
);
}
}
Wrap your widget with ConnectivityWrapper to automatically show a no-internet screen when connectivity is lost:
ConnectivityWrapper (
child: YourWidget (),
offlineWidget: YourCustomOfflineWidget (),
onConnectivityChanged: (status) {
print ('Connectivity status: $status ' );
},
)
The ignoreOfflineState parameter (default: false) controls whether the wrapper automatically shows the no-internet screen:
ConnectivityWrapper (
ignoreOfflineState: true ,
onConnectivityChanged: (status) {
},
child: YourWidget (),
)
When ignoreOfflineState is set to true, the ConnectivityWrapper will not automatically show the no-internet screen when connectivity is lost. Instead, it will continue showing your child widget and notify you of connectivity changes through the onConnectivityChanged callback. This is useful when you want to handle connectivity UI yourself, such as showing snackbars or banners instead of full-screen notifications.
Using ConnectivityMixin
For more fine-grained control, use the ConnectivityMixin in your StatefulWidget:
class _MyScreenState extends State <MyScreen > with ConnectivityMixin {
@override
void onConnectivityChanged (ConnectivityStatus status) {
if (status == ConnectivityStatus .online) {
} else {
}
}
@override
Widget build (BuildContext context) {
if (isOnline) {
return OnlineContent ();
} else {
return OfflineContent ();
}
}
}
Currency Converter
Fittor provides a currency converter utility with live exchange rates.
Basic Conversion
double usdAmount = await context.convertCurrency (
from: 'INR' ,
to: 'USD' ,
amount: 100.0 ,
);
print ('100 INR = $usdAmount USD' );
Convert and Format
String formattedAmount = await context.convertAndFormat (
from: 'INR' ,
to: 'USD' ,
amount: 100.0 ,
);
print ('100 INR = $formattedAmount ' );
Format with Custom Symbols
String formatted = context.formatCurrency (1234.56 , 'USD' );
print (formatted);
Live Exchange Rates
double rate = await context.getExchangeRate ('INR' , 'USD' );
print ('1 INR = $rate USD' );
Advanced Usage
Direct Access to CurrencyConverter
final converter = CurrencyConverter ();
Map <String , dynamic > rates = await converter.getLatestRates ('EUR' );
Map <String , dynamic > cacheInfo = converter.getCacheInfo ();
print ('Last updated: ${cacheInfo['lastUpdated' ]} ' );
FittorCurrency Utilities
final currencyUtils = FittorCurrency ();
Widget priceText = currencyUtils.currencyText (
'\$1,234.56' ,
style: TextStyle (fontSize: 18 , fontWeight: FontWeight .bold),
);
State Management
Overview
Fittor is a pragmatic state management library designed to make Flutter development more efficient with minimal boilerplate. It offers a controller-based approach, more focused API.
Features
Controller-based state management: Create reactive UIs with minimal code
Dependency injection: Easily register and find controllers throughout your app
Reactive value wrappers: Optimized UI updates with fine-grained reactivity
Bindings system: Organize dependencies by route or feature
Extension methods: Access controllers directly from BuildContext
Auto-disposal: Controllers are automatically managed in the widget lifecycle
Getting Started
1. Initialize Fittor
Initialize Fittor at the root of your application:
void main () {
runApp (
FitInitializer (
child: MyApp (),
initialBindings: [AppBindings ()],
),
);
}
2. Create a Controller
Controllers manage your application state and business logic:
class CounterController extends FitController {
int count = 0 ;
void increment () {
count++;
fittor ();
}
@override
void onDelete () {
super .onDelete ();
}
}
3. Using Reactive Values
For more granular updates, use the FitValue class:
class UserController extends FitController {
final username = "" .fit;
final isLoggedIn = false .fit;
void login (String name) {
username.val = name;
isLoggedIn.val = true ;
}
}
4. Register Controllers with Bindings
Create a bindings class to organize your dependencies:
class AppBindings extends FitBindings {
@override
void dependencies () {
lazyPut (() => CounterController ());
lazyPut (() => UserController ());
}
}
5. Using Controllers in Widgets
Access your controllers in the UI using FitBuilder:
FitBuilder <CounterController >(
controller: Fit .find<CounterController >(),
builder: (context, controller) {
return Text ('Count: ${controller.count} ' );
},
)
For reactive values:
FitValueBuilder <String >(
fitValue: userController.username,
builder: (context, username) {
return Text ('Hello, $username ' );
},
)
6. Access Controllers via BuildContext Extension
final controller = context.find<CounterController >();
controller.increment ();
Core Concepts
Controllers
Controllers are the heart of your application logic. Extend FitController to create a controller:
class ThemeController extends FitController {
bool isDarkMode = false ;
void toggleTheme () {
isDarkMode = !isDarkMode;
fittor ();
}
void updateSpecificWidgets () {
fittor ('theme-tag' );
}
}
Dependency Injection
Fittor provides several methods to register and find controllers:
Fit.lazyPut<T>() : Registers a controller for lazy initialization
Fit.put<T>() : Registers an already initialized controller
Fit.find<T>() : Finds a registered controller
Fit.delete<T>() : Removes a controller and calls its onDelete method
Advanced Usage Controller
Tagging Controllers
Register multiple instances of the same controller type:
Fit .put<ApiClient >(ProductApiClient (), tag: 'product' );
Fit .put<ApiClient >(UserApiClient (), tag: 'user' );
final productApi = Fit .find<ApiClient >(tag: 'product' );
final userApi = Fit .find<ApiClient >(tag: 'user' );
Auto-registration with FitBuilder
Controllers can be automatically registered with FitBuilder:
FitBuilder <DashboardController >(
controller: DashboardController (),
autoRegister: true ,
builder: (context, controller) {
return DashboardView (controller: controller);
},
)
Initialization and Disposal
Controllers are automatically disposed when their widgets are removed from the tree. You can also manually dispose controllers:
FitBuilder <VideoController >(
controller: Fit .find<VideoController >(),
init: (controller) => controller.initialize (),
dispose: (controller) => controller.cleanup (),
builder: (context, controller) {
return VideoPlayer (controller);
},
)
Fittor Blur Ash
You can now use the FittorBlurAsh widget to add a blur effect to a widget.
FittorBlurAsh FittorBlurAsh ({
Key ? key,
required String hash,
double? width,
double? height,
BoxFit fit = BoxFit .cover,
Color ? color,
Widget ? child,
Widget ? loadingWidget,
Widget ? errorWidget,
int resolution = 32 ,
})
Usage Example Using Cache Network Image
CachedNetworkImage (
imageUrl: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb' ,
placeholder: (context, url) {
return FittorBlurAsh (
hash: 'UbCP*BWYWWof~qWraykC_3WYjZof?bflaxoL' ,
width: 300 ,
height: 200 ,
);
},
errorWidget: (context, url, error) {
return FittorBlurAsh (
hash: 'UbCP*BWYWWof~qWraykC_3WYjZof?bflaxoL' ,
width: 300 ,
height: 200 ,
);
},
width: 300 ,
height: 200 ,
fit: BoxFit .cover,
),
Read more
Features
✅ Trim by length or lines: Choose between character count or line-based trimming
✅ Rich text support: Use FitReadMore.rich() for complex text formatting
✅ Text annotations: Support for URLs, hashtags, mentions, and custom patterns
✅ Customizable styling: Full control over colors, fonts, and text styles
✅ External control: Control expand/collapse state externally
✅ Callbacks: Get notified when text is expanded or collapsed
✅ Accessibility: Full support for text selection and screen readers
✅ Pre/post text: Add content before and after the main text
FitReadMore (
'Your very long text content goes here...' ,
trimLength: 150 ,
trimCollapsedText: 'Read more' ,
trimExpandedText: 'Show less' ,
colorClickableText: Colors .blue,
)
FitReadMore (
'Check out https://example.com and follow @username #flutter' ,
trimLength: 100 ,
annotations: [
FitAnnotation (
regExp: RegExp (r'https?://[^\s]+' ),
spanBuilder: ({required text, required textStyle}) => TextSpan (
text: text,
style: textStyle.copyWith (
color: Colors .blue,
decoration: TextDecoration .underline,
),
),
),
FitAnnotation (
regExp: RegExp (r'#\w+' ),
spanBuilder: ({required text, required textStyle}) => TextSpan (
text: text,
style: textStyle.copyWith (
color: Colors .blue,
fontWeight: FontWeight .bold,
),
),
),
FitAnnotation (
regExp: RegExp (r'@\w+' ),
spanBuilder: ({required text, required textStyle}) => TextSpan (
text: text,
style: textStyle.copyWith (
color: Colors .purple,
fontWeight: FontWeight .bold,
),
),
),
],
)
Extension
Responsive Features
Extension
Description
Example
num.w
Creates SizedBox with width
20.w creates SizedBox(width: 20)
num.h
Creates SizedBox with height
16.h creates SizedBox(height: 16)
num.s
Creates square SizedBox
24.s creates SizedBox.square(dimension: 24)
context.wp(%)
Percentage of screen width
context.wp(80) gives 80% of screen width
context.hp(%)
Percentage of screen height
context.hp(50) gives 50% of screen height
context.p*
Adaptive padding
context.p16 gives adaptive 16 padding
context.fs*
Adaptive font size
context.fs16 returns responsive font size 16
Connectivity Features
Feature
Description
Example
ConnectivityWrapper
Wraps UI with connectivity monitoring
ConnectivityWrapper(child: MyApp())
ConnectivityMixin
Mixin for StatefulWidgets
class _MyState extends State<MyWidget> with ConnectivityMixin
isOnline property
Check online status with mixin
if (isOnline) { /* do network request */ }
checkConnectivity()
Manual connectivity check
await checkConnectivity()
onConnectivityChanged
Handle status changes
onConnectivityChanged(status) { /* handle change */ }
Currency Features
Feature
Description
Example
convertCurrency()
Convert currency
double usdAmount = await context.convertCurrency(from: 'INR', to: 'USD', amount: 100.0);
convertAndFormat()
Convert and format
String formatted = await context.convertAndFormat(from: 'INR', to: 'USD', amount: 100.0);
formatCurrency()
Format currency
String formatted = context.formatCurrency(1234.56, 'USD');
getExchange Rate()
Get exchange rate
double rate = await context.getExchangeRate('INR', 'USD');
🐞 Troubleshooting
Ensure the package is correctly imported
Check that you're using the latest version
Verify flutter and dart SDK compatibility
Check for any conflicts with other packages
📞 Contact & Support
Author: Mushthak VP
🌐 Connect With Me
💡 Collaboration
Have a project or need custom Flutter development? Feel free to reach out! I'm always open to interesting projects, collaborations, and opportunities.
🤝 Contributing
Contributions are welcome! Whether you're reporting bugs, suggesting improvements, or want to collaborate, don't hesitate to connect.
License
MIT - Copyright © 2025 Mushthak VP
🆘 Support
For any questions, issues, or custom development needs, please contact me directly via email or social media channels.