stretchable_image 0.0.3
stretchable_image: ^0.0.3 copied to clipboard
A Flutter widget that horizontally stretches the center area of an image while keeping the left and right edges visually intact. Useful for chat bubbles, resizable panels, and border-like images.
stretchable_image #
A Flutter widget that horizontally stretches the center area of an image while keeping the left and right edges visually intact. This is especially useful for:
- Chat bubbles
- Resizable panels / cards
- Border-like images that need to adapt to dynamic width
Features #
- Split image into three parts: left / center (stretch area) / right
- Only center area is stretched or cropped horizontally
- Pixel-perfect with device pixel ratio handling
- Flexible sizing:
- Fixed size via
size - Or automatically adapts to parent constraints when
sizeisnull
- Fixed size via
Screenshot #
Example with different target widths (using border.png):

Installation #
Add this to your pubspec.yaml:
stretchable_image: ^{latestVersion}
Usage #
1. Basic usage #
Use fixed size if widget is bounded.
When size is null, the widget will:
- Use
LayoutBuilderto read parent constraints - Fill the available width/height if bounded
- Fall back to the image’s intrinsic size if unbounded
import 'package:stretchable_image/stretchable_image.dart';
class BasicExample extends StatelessWidget {
const BasicExample({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(200, 40),
),
);
}
}
2. Custom stretch area ratio #
centerStretchAreaRatio controls how wide the stretchable center area is (relative to the whole image width).The default value is 0.5 ,and it must be less than 1.0,
class CustomStretchAreaExample extends StatelessWidget {
const CustomStretchAreaExample({super.key});
@override
Widget build(BuildContext context) {
return Column(
children: const [
Text('Center stretch ratio = 0.3'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(200, 40),
centerStretchAreaRatio: 0.3,
),
SizedBox(height: 16),
Text('Center stretch ratio = 0.6'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(200, 40),
centerStretchAreaRatio: 0.6,
),
],
);
}
}
Example from this package #
The example app (example/lib/main.dart) can look like this:
import 'package:flutter/material.dart';
import 'package:stretchable_image/stretchable_image.dart';
void main() {
runApp(const StretchableImageExampleApp());
}
class StretchableImageExampleApp extends StatelessWidget {
const StretchableImageExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'StretchableImage Example',
home: const StretchableImageDemoPage(),
debugShowCheckedModeBanner: false,
);
}
}
class StretchableImageDemoPage extends StatelessWidget {
const StretchableImageDemoPage({super.key});
@override
Widget build(BuildContext context) {
const double height = 40;
return Scaffold(
appBar: AppBar(
title: const Text('StretchableImage Demo'),
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'border.png as background. '
'Show how the image behaves with different widths.',
textAlign: TextAlign.center,
),
SizedBox(height: 24),
Text('Narrow (compressed, middle area may be clipped)'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(80, height),
),
SizedBox(height: 24),
Text('Normal width (close to original aspect)'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(160, height),
),
SizedBox(height: 24),
Text('Wide (middle area is stretched)'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(260, height),
),
SizedBox(height: 24),
Text('Extra wide (strong stretch in the middle)'),
SizedBox(height: 8),
StretchableImage(
image: AssetImage('assets/border.png'),
size: Size(360, height),
),
],
),
),
),
);
}
}
API #
class StretchableImage extends StatefulWidget {
/// Source image.
final ImageProvider image;
/// Stretch area ratio, must be less than 1.0. Default is 0.5.
///
/// The image is conceptually split into three parts:
/// [ left | center | right ]
/// where:
/// - `center` width = `centerStretchAreaRatio * imageWidth`
/// - `left` and `right` share the remaining width equally
///
/// When the widget is stretched horizontally:
/// - `center` area is stretched
/// - `left` and `right` keep their visual shape
///
/// When the widget is compressed horizontally:
/// - `center` area is cropped first
/// - `left` and `right` are preserved as much as possible
final double centerStretchAreaRatio;
/// Target size.
///
/// - If provided, [StretchableImage] will paint with this size exactly.
/// - If null, the widget will expand to the constraints from its parent
/// (using [LayoutBuilder]) and use that as the painting size.
final Size? size;
const StretchableImage({
Key? key,
required this.image,
this.size,
this.centerStretchAreaRatio = 0.5,
});
}
License #
MIT License. See LICENSE for details.