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), // 50% of screen width
height: context.hp(25), // 25% of screen height
padding: EdgeInsets.all(context.p16), // Adaptive padding
child: Text(
'Responsive Text',
style: TextStyle(fontSize: context.fs18), // Adaptive font size
),
)
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), // 50% of screen width
padding: EdgeInsets.all(p16), // Predefined padding
child: Text(
'Hello World',
style: TextStyle(fontSize: fs(18)), // Responsive font size
),
);
}
}
Custom Sized Box
Create SizedBox widgets with simple extensions:
// Width SizedBox
20.w // SizedBox with width 20
// Height SizedBox
16.h // SizedBox with height 16
// Square SizedBox
24.s // SizedBox with width and height both 24
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(),
// Optional customizations:
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, // Don't show no-internet screen automatically
onConnectivityChanged: (status) {
// Handle connectivity changes yourself
},
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) {
// Handle online state
} else {
// Handle offline state
}
}
@override
Widget build(BuildContext context) {
// Access connectivity status with:
if (isOnline) {
return OnlineContent();
} else {
return OfflineContent();
}
}
}
Currency Converter
Fittor provides a currency converter utility with live exchange rates.
Basic Conversion
// Convert 100 INR to USD
double usdAmount = await context.convertCurrency(
from: 'INR',
to: 'USD',
amount: 100.0,
);
print('100 INR = $usdAmount USD');
Convert and Format
// Convert and format with currency symbol
String formattedAmount = await context.convertAndFormat(
from: 'INR',
to: 'USD',
amount: 100.0,
);
print('100 INR = $formattedAmount'); // Outputs: 100 INR = $1.17
Format with Custom Symbols
// Format a currency amount with proper symbol
String formatted = context.formatCurrency(1234.56, 'USD');
print(formatted); // Outputs: $1,234.56
Live Exchange Rates
// Get the current exchange rate between two currencies
double rate = await context.getExchangeRate('INR', 'USD');
print('1 INR = $rate USD');
Advanced Usage
Direct Access to CurrencyConverter
final converter = CurrencyConverter();
// Manually get latest rates for a base currency
Map<String, dynamic> rates = await converter.getLatestRates('EUR');
// Check cache status
Map<String, dynamic> cacheInfo = converter.getCacheInfo();
print('Last updated: ${cacheInfo['lastUpdated']}');
FittorCurrency Utilities
final currencyUtils = FittorCurrency();
// Create a currency text widget
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(); // Notify listeners to rebuild
}
@override
void onDelete() {
// Clean up resources when controller is removed
super.onDelete();
}
}
3. Using Reactive Values
For more granular updates, use the FitValue
class:
class UserController extends FitController {
final username = "".fit; // Creates a FitValue<String>
final isLoggedIn = false.fit; // Creates a FitValue<bool>
void login(String name) {
username.val = name; // This will automatically update listeners
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(); // Notify all listeners (will rebuild UI)
}
// To update specific widgets only
void updateSpecificWidgets() {
fittor('theme-tag'); // Only rebuilds widgets with 'theme-tag'
}
}
Dependency Injection
Fittor provides several methods to register and find controllers:
Fit.lazyPut<T>()
: Registers a controller for lazy initializationFit.put<T>()
: Registers an already initialized controllerFit.find<T>()
: Finds a registered controllerFit.delete<T>()
: Removes a controller and calls its onDelete method
Advanced Usage Controller
Tagging Controllers
Register multiple instances of the same controller type:
// Registration
Fit.put<ApiClient>(ProductApiClient(), tag: 'product');
Fit.put<ApiClient>(UserApiClient(), tag: 'user');
// Usage
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.
// Usage
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: [
// URL annotation
FitAnnotation(
regExp: RegExp(r'https?://[^\s]+'),
spanBuilder: ({required text, required textStyle}) => TextSpan(
text: text,
style: textStyle.copyWith(
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
),
// Hashtag annotation
FitAnnotation(
regExp: RegExp(r'#\w+'),
spanBuilder: ({required text, required textStyle}) => TextSpan(
text: text,
style: textStyle.copyWith(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
),
// Mention annotation
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
- Email: mail.musthak@gmail.com
- WhatsApp: +919061213930
- LinkedIn: Mushthak VP
- Instagram: @musth4k
- GitHub: mushthakvp
💡 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.
Libraries
- api/client/base_client
- api/client/universal_client
- api/index
- api/interceptors/base_interceptor
- api/interceptors/logging_interceptor
- api/interceptors/retry_interceptor
- api/models/exceptions
- api/models/headers
- api/models/request
- api/models/response
- api/platform/mobile_adapter
- api/platform/platform_adapter
- api/platform/platform_adapter_stub
- api/platform/web_adapter
- api/utils/constants
- api/utils/helpers
- api/utils/validators
- api/wasm/wasm_bridge
- api/wasm/wasm_bridge_stub
- api/wasm/wasm_bridge_web
- blur_ash/blur_ash
- blur_ash/blur_hash
- blur_ash/index
- code_view/fittor_code
- A beautiful, colorful code viewer widget for Flutter with syntax highlighting and copy/launch functionality.
- code_view/index
- code_view/models/clipboard_utils
- code_view/models/line_result
- code_view/src/code_view_widget
- code_view/src/color_schemes
- code_view/src/syntax_highlighter
- connectivity/connectivity_manager
- connectivity/connectivity_mixin
- connectivity/connectivity_wrapper
- connectivity/index
- connectivity/no_internet_page
- currency/currency_converter
- currency/currency_extensions
- currency/fittor_currency
- currency/index
- fit-state/builder/fit_builder
- fit-state/core/fit_core
- fit-state/explore/fit_explore
- fit-state/index
- fit-state/selector/fit_selector
- fittor
- read_more/annotation
- read_more/fit_read_more
- read_more/index
- read_more/text_span_utils
- read_more/trim_mode