Introduction
FractionallySizedBox is a Flutter widget that sizes its child to a fraction of its parent’s total available space. It’s particularly useful when you need to create responsive layouts or components that need to maintain proportional dimensions.
Understanding FractionallySizedBox
Key Properties
widthFactor
: A multiplier for the parent’s width (0.0 to 1.0)heightFactor
: A multiplier for the parent’s height (0.0 to 1.0)alignment
: Determines how to position the child within the boxchild
: The widget to be sized
When to Use FractionallySizedBox
- Creating responsive progress indicators
- Building dynamic loading bars
- Implementing proportional layouts
- Making adaptive UI components
Practical Example: Custom Progress Bar
Here’s a real-world example of a progress bar component that you might use for file uploads, data processing, or loading states:
class ProgressBar extends StatelessWidget {
final double progress; // Value from 0.0 to 1.0
final String label;
final Color color;
final Color backgroundColor;
final double height;
const ProgressBar({
Key? key,
required this.progress,
required this.label,
this.color = Colors.blue,
this.backgroundColor = Colors.grey,
this.height = 20,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Progress Label
Text(
label,
style: Theme.of(context).textTheme.bodyMedium,
),
SizedBox(height: 8),
// Progress Bar Container
Container(
height: height,
width: double.infinity,
decoration: BoxDecoration(
color: backgroundColor.withOpacity(0.2),
borderRadius: BorderRadius.circular(height / 2),
),
clipBehavior: Clip.hardEdge,
child: FractionallySizedBox(
widthFactor: progress.clamp(0.0, 1.0),
alignment: Alignment.centerLeft,
child: Container(
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(height / 2),
),
child: Center(
child: Text(
'${(progress * 100).toInt()}%',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: height * 0.6,
),
),
),
),
),
),
],
);
}
}
Usage Example
Here’s how to implement the progress bar in a real application:
class UploadScreen extends StatefulWidget {
@override
_UploadScreenState createState() => _UploadScreenState();
}
class _UploadScreenState extends State<UploadScreen> {
double _uploadProgress = 0.0;
// Simulate a file upload process
void _simulateUpload() {
setState(() => _uploadProgress = 0.0);
Timer.periodic(Duration(milliseconds: 100), (timer) {
setState(() {
_uploadProgress += 0.01;
if (_uploadProgress >= 1.0) {
timer.cancel();
_showCompleteDialog();
}
});
});
}
void _showCompleteDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Upload Complete'),
content: Text('Your file has been successfully uploaded.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('OK'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('File Upload Example'),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Single file upload progress
ProgressBar(
progress: _uploadProgress,
label: 'Uploading file...',
color: Theme.of(context).primaryColor,
),
SizedBox(height: 24),
// Multiple progress bars example
ProgressBar(
progress: _uploadProgress * 0.7, // Different progress rates
label: 'Processing data...',
color: Colors.green,
height: 15,
),
SizedBox(height: 24),
// Action buttons
Center(
child: ElevatedButton(
onPressed: _uploadProgress < 1.0 ? _simulateUpload : null,
child: Text('Start Upload'),
),
),
],
),
),
);
}
}
Best Practices
- Input Validation
- Always clamp progress values between 0.0 and 1.0
- Handle edge cases (null values, negative numbers)
- Provide meaningful default values
- Performance
// DO: Use const constructor when possible const ProgressBar( progress: 0.5, label: 'Loading...', ) // DON'T: Rebuild unnecessarily ProgressBar( progress: 0.5, label: 'Loading...', )
- Accessibility
- Use appropriate colors with good contrast
- Consider adding semantic labels
- Support different text scales
Common Pitfalls to Avoid
- Unbounded Width
// DON'T: May cause layout errors Row( children: [ FractionallySizedBox( widthFactor: 0.5, child: ProgressBar(...), ), ], ) // DO: Provide constraints Row( children: [ Expanded( child: FractionallySizedBox( widthFactor: 0.5, child: ProgressBar(...), ), ), ], )
- Animation Smoothness
// DON'T: Jerky updates setState(() { progress += 0.1; }); // DO: Smooth transitions setState(() { progress = (progress + 0.1).clamp(0.0, 1.0); });
Conclusion
FractionallySizedBox is an essential widget for creating responsive components in Flutter. The progress bar example demonstrates how it can be used effectively to create a reusable, professional-looking component. By following the best practices and avoiding common pitfalls, you can create robust and maintainable UI components that work well across different screen sizes and orientations.

