Skip to content

Instantly share code, notes, and snippets.

@cristinaponcela
Created October 22, 2024 15:40
Show Gist options
  • Select an option

  • Save cristinaponcela/3053d28b0ba280e2b61da5e34b8e5203 to your computer and use it in GitHub Desktop.

Select an option

Save cristinaponcela/3053d28b0ba280e2b61da5e34b8e5203 to your computer and use it in GitHub Desktop.
How to create a custom InnerShadow widget in Flutter. Overrides the RenderProxyBox class to add a smooth inner shadow to any child widget using the canvas' paint and save/restore techniques. MIT License
// Custom widget for adding inner shadow
class InnerShadow extends SingleChildRenderObjectWidget {
const InnerShadow({
Key? key,
this.blur = 5, // Default blur value for shadow
this.color = Colors.black38, // Default shadow color
this.offset = const Offset(50, 50), // Default shadow offset
Widget? child,
}) : super(key: key, child: child);
final double blur; // Amount of blur for the shadow
final Color color; // Color of the shadow
final Offset offset; // Offset of the shadow
@override
RenderObject createRenderObject(BuildContext context) {
// Create and return the custom render object
final renderObject = _RenderInnerShadow();
updateRenderObject(context, renderObject);
return renderObject;
}
@override
void updateRenderObject(
BuildContext context, _RenderInnerShadow renderObject) {
// Update the render object with the new values of blur, color, and offset
renderObject
..color = color
..blur = blur
..dx = offset.dx
..dy = offset.dy;
}
}
// Custom render object class for inner shadow effect
class _RenderInnerShadow extends RenderProxyBox {
double blur = 5; // Default blur for shadow
Color color = Color(0xFFF15923); // Default color for shadow
double dx = 5; // Default x-axis translation for shadow
double dy = 5; // Default y-axis translation for shadow
@override
void paint(PaintingContext context, Offset offset) {
// Ensure there is a child to paint
if (child == null) return;
// Calculate the outer and inner bounds of the shadow
final Rect outerRect = offset & size;
final Rect innerRect =
outerRect.deflate(blur); // Adjust the inner rect for shadow
final Canvas canvas = context.canvas;
// Draw the child widget
canvas.saveLayer(outerRect, Paint());
context.paintChild(child!, offset);
// Create the paint for the shadow layer
final Paint shadowPaint = Paint()
..blendMode = BlendMode.srcATop // Apply the shadow over the widget
..colorFilter =
ColorFilter.mode(color, BlendMode.srcOut) // Set shadow color
..imageFilter =
ImageFilter.blur(sigmaX: blur, sigmaY: blur); // Apply blur
// Draw the shadow layer on the canvas
canvas.saveLayer(outerRect, shadowPaint);
context.paintChild(child!, offset);
// Restore the canvas stack
canvas.restore();
canvas.restore();
}
}
import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:flutter/rendering.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.white, // White background for contrast
body: Center(
// Center the widget
child: InnerShadow(
blur: 15,
color: Color.fromARGB(255, 70, 70, 70),
offset: const Offset(5, 5), // Apply the shadow offset
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8)),
color: Color(0xFFF15923), // The orange background color
),
height: 100,
width: 200,
child: const Center(
child: Text(
"Test", // Centered white text
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
);
}
}
// Custom widget for adding inner shadow
class InnerShadow extends SingleChildRenderObjectWidget {
const InnerShadow({
Key? key,
this.blur = 5, // Default blur value for shadow
this.color = Colors.black38, // Default shadow color
this.offset = const Offset(50, 50), // Default shadow offset
Widget? child,
}) : super(key: key, child: child);
final double blur; // Amount of blur for the shadow
final Color color; // Color of the shadow
final Offset offset; // Offset of the shadow
@override
RenderObject createRenderObject(BuildContext context) {
// Create and return the custom render object
final renderObject = _RenderInnerShadow();
updateRenderObject(context, renderObject);
return renderObject;
}
@override
void updateRenderObject(
BuildContext context, _RenderInnerShadow renderObject) {
// Update the render object with the new values of blur, color, and offset
renderObject
..color = color
..blur = blur
..dx = offset.dx
..dy = offset.dy;
}
}
// Custom render object class for inner shadow effect
class _RenderInnerShadow extends RenderProxyBox {
double blur = 5; // Default blur for shadow
Color color = Color(0xFFF15923); // Default color for shadow
double dx = 5; // Default x-axis translation for shadow
double dy = 5; // Default y-axis translation for shadow
@override
void paint(PaintingContext context, Offset offset) {
// Ensure there is a child to paint
if (child == null) return;
// Calculate the outer and inner bounds of the shadow
final Rect outerRect = offset & size;
final Rect innerRect =
outerRect.deflate(blur); // Adjust the inner rect for shadow
final Canvas canvas = context.canvas;
// Draw the child widget
canvas.saveLayer(outerRect, Paint());
context.paintChild(child!, offset);
// Create the paint for the shadow layer
final Paint shadowPaint = Paint()
..blendMode = BlendMode.srcATop // Apply the shadow over the widget
..colorFilter =
ColorFilter.mode(color, BlendMode.srcOut) // Set shadow color
..imageFilter =
ImageFilter.blur(sigmaX: blur, sigmaY: blur); // Apply blur
// Draw the shadow layer on the canvas
canvas.saveLayer(outerRect, shadowPaint);
context.paintChild(child!, offset);
// Restore the canvas stack
canvas.restore();
canvas.restore();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment