quickshadowflutter 0.0.5
quickshadowflutter: ^0.0.5 copied to clipboard
A Flutter package that adds inner shadow support to any widget. Supports BorderRadius, multiple shadows, and works with Container, Button, Card, or any custom widget. Perfect for neumorphism UI.
import 'package:flutter/material.dart';
import 'package:quickshadowflutter/quickshadowflutter.dart';
void main() {
runApp(const QuickShadowExampleApp());
}
class QuickShadowExampleApp extends StatelessWidget {
const QuickShadowExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Quick Shadow Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(colorSchemeSeed: Colors.deepPurple, useMaterial3: true),
home: const DemoHomePage(),
);
}
}
class DemoHomePage extends StatelessWidget {
const DemoHomePage({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
backgroundColor: const Color(0xFFE0E5EC),
// backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: const Color(0xFFE0E5EC),
title: const Text('Quick Shadow Demo'),
bottom: const TabBar(
isScrollable: true,
tabs: [
Tab(text: 'Basic'),
Tab(text: 'Neumorphism'),
Tab(text: 'Buttons'),
Tab(text: 'Cards'),
],
),
),
body: const TabBarView(
children: [
BasicShadowDemo(),
NeumorphismDemo(),
ButtonsDemo(),
CardsDemo(),
],
),
),
);
}
}
// ─────────────────────────────────────────────
// TAB 1: Basic shadows
// ─────────────────────────────────────────────
class BasicShadowDemo extends StatelessWidget {
const BasicShadowDemo({super.key});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Top-Left Shadow'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(
color: Color(0x66000000),
blurRadius: 12,
offset: Offset(6, 6),
),
],
borderRadius: BorderRadius.circular(16),
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(16),
),
),
),
const SizedBox(height: 32),
const _SectionTitle('Bottom-Right Shadow'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(
color: Color(0x66000000),
blurRadius: 12,
offset: Offset(-6, -6),
),
],
borderRadius: BorderRadius.circular(16),
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(16),
),
),
),
const SizedBox(height: 32),
const _SectionTitle('Circular Shape'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(
color: Color(0x66000000),
blurRadius: 16,
offset: Offset(0, 0),
),
],
borderRadius: BorderRadius.circular(75),
child: Container(
width: 150,
height: 150,
decoration: const BoxDecoration(
color: Color(0xFFE0E5EC),
shape: BoxShape.circle,
),
),
),
],
),
);
}
}
// ─────────────────────────────────────────────
// TAB 2: Neumorphism
// ─────────────────────────────────────────────
class NeumorphismDemo extends StatelessWidget {
const NeumorphismDemo({super.key});
static const bgColor = Color(0xFFE0E5EC);
static const darkShadow = Color(0xFFA3B1C6);
static const lightShadow = Color(0xFFFFFFFF);
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
print("MediaQuery.of(context).size.width: Mobile: ${MediaQuery.of(context).size.width}");
return SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Pressed / Inset Effect'),
const SizedBox(height: 16),
// Neumorphism "pressed" button using inner shadow
QuickShadow(
shadows: const [
Shadow(color: darkShadow, blurRadius: 10, offset: Offset(5, 5)),
Shadow(
color: lightShadow,
blurRadius: 10,
offset: Offset(-5, -5),
),
],
borderRadius: BorderRadius.circular(20),
child: Container(
width: 160,
height: 60,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(20),
),
alignment: Alignment.center,
child: const Text(
'PRESSED',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xFF6E7F9B),
letterSpacing: 2,
),
),
),
),
const SizedBox(height: 32),
const _SectionTitle('Inset Circle'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(color: darkShadow, blurRadius: 14, offset: Offset(6, 6)),
Shadow(
color: lightShadow,
blurRadius: 14,
offset: Offset(-6, -6),
),
],
borderRadius: BorderRadius.circular(80),
child: Container(
width: 160,
height: 160,
decoration: const BoxDecoration(
color: bgColor,
shape: BoxShape.circle,
),
alignment: Alignment.center,
child: const Icon(
Icons.play_arrow_rounded,
size: 50,
color: Color(0xFF6E7F9B),
),
),
),
const SizedBox(height: 32),
const _SectionTitle('Inset Text Field'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(color: darkShadow, blurRadius: 8, offset: Offset(4, 4)),
Shadow(color: lightShadow, blurRadius: 8, offset: Offset(-4, -4)),
],
borderRadius: BorderRadius.circular(14),
child: TextFormField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Enter the text",
constraints: BoxConstraints(
maxWidth: size.width > 400 ? size.width * 0.2 : size.width * 0.6,
maxHeight: 156,
),
contentPadding: EdgeInsets.all(18),
),
),
),
],
),
);
}
}
// ─────────────────────────────────────────────
// TAB 3: Buttons
// ─────────────────────────────────────────────
class ButtonsDemo extends StatefulWidget {
const ButtonsDemo({super.key});
@override
State<ButtonsDemo> createState() => _ButtonsDemoState();
}
class _ButtonsDemoState extends State<ButtonsDemo> {
bool _isPressed = false;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Tap to Toggle Pressed State'),
const SizedBox(height: 24),
GestureDetector(
onTapDown: (_) => setState(() => _isPressed = true),
onTapUp: (_) => setState(() => _isPressed = false),
onTapCancel: () => setState(() => _isPressed = false),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 100),
child: _isPressed
? QuickShadow(
key: const ValueKey('pressed'),
shadows: const [
Shadow(
color: Color(0x66000000),
blurRadius: 10,
offset: Offset(4, 4),
),
],
borderRadius: BorderRadius.circular(14),
child: const _ButtonBox(label: 'PRESSED ✓'),
)
: Container(
key: const ValueKey('normal'),
width: 160,
height: 56,
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(14),
boxShadow: const [
BoxShadow(
color: Color(0xFFA3B1C6),
blurRadius: 10,
offset: Offset(5, 5),
),
BoxShadow(
color: Color(0xFFFFFFFF),
blurRadius: 10,
offset: Offset(-5, -5),
),
],
),
alignment: Alignment.center,
child: const Text(
'TAP ME',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xFF6E7F9B),
letterSpacing: 2,
),
),
),
),
),
const SizedBox(height: 40),
const _SectionTitle('QuickShadowContainer Shorthand'),
const SizedBox(height: 16),
QuickShadowContainer(
width: 200,
height: 56,
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(14),
),
shadows: const [
Shadow(
color: Color(0x55000000),
blurRadius: 10,
offset: Offset(4, 4),
),
],
alignment: Alignment.center,
child: const Text(
'QuickShadowContainer',
style: TextStyle(color: Color(0xFF6E7F9B)),
),
),
],
),
);
}
}
class _ButtonBox extends StatelessWidget {
final String label;
const _ButtonBox({required this.label});
@override
Widget build(BuildContext context) {
return Container(
width: 160,
height: 56,
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(14),
),
alignment: Alignment.center,
child: Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Color(0xFF6E7F9B),
letterSpacing: 2,
),
),
);
}
}
// ─────────────────────────────────────────────
// TAB 4: Cards
// ─────────────────────────────────────────────
class CardsDemo extends StatelessWidget {
const CardsDemo({super.key});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _SectionTitle('Inset Card'),
const SizedBox(height: 16),
QuickShadow(
shadows: const [
Shadow(
color: Color(0x55000000),
blurRadius: 14,
offset: Offset(6, 6),
),
Shadow(
color: Color(0xAAFFFFFF),
blurRadius: 14,
offset: Offset(-6, -6),
),
],
borderRadius: BorderRadius.circular(20),
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(20),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Inner Shadow Card',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF4A5568),
),
),
SizedBox(height: 8),
Text(
'This card uses QuickShadow to create an inset/pressed effect. Works with any BorderRadius.',
style: TextStyle(color: Color(0xFF718096)),
),
],
),
),
),
const SizedBox(height: 32),
const _SectionTitle('Stats Widget'),
const SizedBox(height: 16),
const Row(
children: [
Expanded(
child: _StatCard(value: '128', label: 'Likes'),
),
SizedBox(width: 16),
Expanded(
child: _StatCard(value: '4.9', label: 'Rating'),
),
SizedBox(width: 16),
Expanded(
child: _StatCard(value: '2k', label: 'Downloads'),
),
],
),
],
),
);
}
}
class _StatCard extends StatelessWidget {
final String value;
final String label;
const _StatCard({required this.value, required this.label});
@override
Widget build(BuildContext context) {
return QuickShadow(
shadows: const [
Shadow(color: Color(0x44000000), blurRadius: 10, offset: Offset(4, 4)),
Shadow(
color: Color(0x88FFFFFF),
blurRadius: 10,
offset: Offset(-4, -4),
),
],
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20),
decoration: BoxDecoration(
color: const Color(0xFFE0E5EC),
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
Text(
value,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Color(0xFF4A5568),
),
),
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(fontSize: 12, color: Color(0xFF9AA5B4)),
),
],
),
),
);
}
}
// ─────────────────────────────────────────────
// Shared
// ─────────────────────────────────────────────
class _SectionTitle extends StatelessWidget {
final String text;
const _SectionTitle(this.text);
@override
Widget build(BuildContext context) {
return Text(
text,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF4A5568),
letterSpacing: 0.5,
),
);
}
}