extended_wrapper 1.0.0
extended_wrapper: ^1.0.0 copied to clipboard
A Flutter package that provides customizable wrapper widgets with spine/tail designs, perfect for creating chat bubbles, tooltips, and other UI elements that need directional indicators.
example/lib/main.dart
import 'package:extended_wrapper/extended_wrapper.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const ExtendedWrapperExampleApp());
}
class ExtendedWrapperExampleApp extends StatelessWidget {
const ExtendedWrapperExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Extended Wrapper Example',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const ExampleHomePage(),
);
}
}
class ExampleHomePage extends StatefulWidget {
const ExampleHomePage({super.key});
@override
State<ExampleHomePage> createState() => _ExampleHomePageState();
}
class _ExampleHomePageState extends State<ExampleHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Extended Wrapper Examples'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionTitle('Basic Examples'),
const SizedBox(height: 16),
// Basic wrapper
_buildExampleCard(
'Basic Wrapper',
const Wrapper(
color: Colors.blue,
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
'Basic wrapper with default settings',
style: TextStyle(color: Colors.white),
),
),
),
),
// Simple wrapper without spine
_buildExampleCard(
'Simple Wrapper (No Spine)',
Wrapper.just(
color: Colors.orange,
radius: 12.0,
shadow: BoxShadow(
color: Colors.orange.withValues(alpha: 0.3),
blurRadius: 12,
spreadRadius: 2,
),
child: const Padding(
padding: EdgeInsets.all(16),
child: Text('Simple wrapper without spine'),
),
),
),
_buildSectionTitle('Chat Bubble Examples'),
const SizedBox(height: 16),
// Chat bubble with left spine
_buildExampleCard(
'Chat Bubble (Left Spine)',
const Row(
children: [
Expanded(
child: Wrapper(
spineType: SpineType.left,
spineHeight: 12.0,
angle: 60,
color: Colors.blue,
shadow: BoxShadow(
color: Colors.black26,
blurRadius: 8,
offset: Offset(2, 2),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Text(
'This is a chat message with a left spine',
style: TextStyle(color: Colors.white),
),
),
),
),
SizedBox(width: 50),
],
),
),
// Chat bubble with right spine
_buildExampleCard(
'Chat Bubble (Right Spine)',
const Row(
children: [
SizedBox(width: 50),
Expanded(
child: Wrapper(
spineType: SpineType.right,
spineHeight: 12.0,
angle: 60,
color: Colors.green,
shadow: BoxShadow(
color: Colors.black26,
blurRadius: 8,
offset: Offset(2, 2),
),
child: Padding(
padding: EdgeInsets.all(12),
child: Text(
'This is a chat message with a right spine',
style: TextStyle(color: Colors.white),
),
),
),
),
],
),
),
_buildSectionTitle('Tooltip Examples'),
const SizedBox(height: 16),
// Tooltip with top spine
_buildExampleCard(
'Tooltip (Top Spine)',
Center(
child: Column(
children: [
Wrapper(
spineType: SpineType.top,
spineHeight: 8.0,
angle: 45,
color: Colors.grey[800]!,
radius: 8.0,
child: const Padding(
padding: EdgeInsets.all(8),
child: Text(
'This is a tooltip with a top spine',
style: TextStyle(color: Colors.white),
),
),
),
const SizedBox(height: 20),
const Text('Hover over this text'),
],
),
),
),
// Tooltip with bottom spine
_buildExampleCard(
'Tooltip (Bottom Spine)',
const Center(
child: Column(
children: [
Text('Hover over this text'),
SizedBox(height: 20),
Wrapper(
spineType: SpineType.bottom,
spineHeight: 8.0,
angle: 45,
color: Colors.purple,
radius: 8.0,
child: Padding(
padding: EdgeInsets.all(8),
child: Text(
'This is a tooltip with a bottom spine',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
_buildSectionTitle('Custom Spine Examples'),
const SizedBox(height: 16),
// Custom triangle spine
_buildExampleCard(
'Custom Triangle Spine',
Wrapper(
spineType: SpineType.right,
spinePathBuilder: (canvas, spineType, range) {
return Path()
..moveTo(range.left, range.top)
..lineTo(range.right, range.center.dy)
..lineTo(range.left, range.bottom)
..close();
},
color: Colors.red,
child: const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Custom triangle spine!',
style: TextStyle(color: Colors.white),
),
),
),
),
// Custom curved spine
_buildExampleCard(
'Custom Curved Spine',
Wrapper(
spineType: SpineType.left,
spinePathBuilder: (canvas, spineType, range) {
return Path()
..moveTo(range.right, range.top)
..quadraticBezierTo(
range.left,
range.center.dy,
range.right,
range.bottom,
)
..close();
},
color: Colors.teal,
child: const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Custom curved spine!',
style: TextStyle(color: Colors.white),
),
),
),
),
_buildSectionTitle('Advanced Examples'),
const SizedBox(height: 16),
// Wrapper with stroke
_buildExampleCard(
'Wrapper with Stroke',
const Wrapper(
color: Colors.yellow,
strokeWidth: 2.0,
shadow: BoxShadow(
color: Colors.black26,
blurRadius: 4,
offset: Offset(1, 1),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Text('Wrapper with border stroke'),
),
),
),
// Wrapper with end positioning
_buildExampleCard(
'End Positioned Spine',
const Wrapper(
spineType: SpineType.left,
formEnd: true,
color: Colors.indigo,
child: Padding(
padding: EdgeInsets.all(16),
child: Text(
'Spine positioned at the end',
style: TextStyle(color: Colors.white),
),
),
),
),
// Wrapper with different angles
_buildExampleCard(
'Different Spine Angles',
const Row(
children: [
Expanded(
child: Wrapper(
spineType: SpineType.left,
angle: 30,
color: Colors.pink,
child: Padding(
padding: EdgeInsets.all(8),
child: Text(
'30°',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
),
SizedBox(width: 8),
Expanded(
child: Wrapper(
spineType: SpineType.left,
angle: 60,
color: Colors.pink,
child: Padding(
padding: EdgeInsets.all(8),
child: Text(
'60°',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
),
SizedBox(width: 8),
Expanded(
child: Wrapper(
spineType: SpineType.left,
angle: 90,
color: Colors.pink,
child: Padding(
padding: EdgeInsets.all(8),
child: Text(
'90°',
style: TextStyle(color: Colors.white),
textAlign: TextAlign.center,
),
),
),
),
],
),
),
const SizedBox(height: 32),
],
),
),
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
);
}
Widget _buildExampleCard(String title, Widget example) {
return Card(
margin: const EdgeInsets.only(bottom: 16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
example,
],
),
),
);
}
}