Skip to content

Instantly share code, notes, and snippets.

@Vanchel
Last active November 8, 2024 08:01
Show Gist options
  • Save Vanchel/d473ef4204ab54b6203020bc4f43c994 to your computer and use it in GitHub Desktop.
Save Vanchel/d473ef4204ab54b6203020bc4f43c994 to your computer and use it in GitHub Desktop.
Border with label placed inside (Flutter 3.22)
import 'package:flutter/material.dart';
class OutlinedInputBorder extends InputBorder {
/// Creates a rounded rectangle outline border for an [InputDecorator].
///
/// If the [borderSide] parameter is [BorderSide.none], it will not draw a
/// border. However, it will still define a shape (which you can see if
/// [InputDecoration.filled] is true).
///
/// If an application does not specify a [borderSide] parameter of
/// value [BorderSide.none], the input decorator substitutes its own, using
/// [copyWith], based on the current theme and [InputDecorator.isFocused].
///
/// The [borderRadius] parameter defaults to a value where all four
/// corners have a circular radius of 4.0. The [borderRadius] parameter
/// must not be null and the corner radii must be circular, i.e. their
/// [Radius.x] and [Radius.y] values must be the same.
///
/// See also:
///
/// * [InputDecoration.floatingLabelBehavior], which should be set to
/// [FloatingLabelBehavior.never] when the [borderSide] is
/// [BorderSide.none]. If let as [FloatingLabelBehavior.auto], the label
/// will extend beyond the container as if the border were still being
/// drawn.
const OutlinedInputBorder({
super.borderSide = const BorderSide(),
this.borderRadius = const BorderRadius.all(Radius.circular(4.0)),
});
/// The radii of the border's rounded rectangle corners.
///
/// The corner radii must be circular, i.e. their [Radius.x] and [Radius.y]
/// values must be the same.
final BorderRadius borderRadius;
@override
bool get isOutline => false;
@override
OutlinedInputBorder copyWith({
BorderSide? borderSide,
BorderRadius? borderRadius,
}) {
return OutlinedInputBorder(
borderSide: borderSide ?? this.borderSide,
borderRadius: borderRadius ?? this.borderRadius,
);
}
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(borderSide.width);
}
@override
OutlinedInputBorder scale(double t) {
return OutlinedInputBorder(
borderSide: borderSide.scale(t),
borderRadius: borderRadius * t,
);
}
@override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
if (a is OutlinedInputBorder) {
final OutlinedInputBorder outline = a;
return OutlinedInputBorder(
borderRadius: BorderRadius.lerp(outline.borderRadius, borderRadius, t)!,
borderSide: BorderSide.lerp(outline.borderSide, borderSide, t),
);
}
return super.lerpFrom(a, t);
}
@override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
if (b is OutlinedInputBorder) {
final OutlinedInputBorder outline = b;
return OutlinedInputBorder(
borderRadius: BorderRadius.lerp(borderRadius, outline.borderRadius, t)!,
borderSide: BorderSide.lerp(borderSide, outline.borderSide, t),
);
}
return super.lerpTo(b, t);
}
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..addRRect(borderRadius
.resolve(textDirection)
.toRRect(rect)
.deflate(borderSide.width));
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
void paintInterior(Canvas canvas, Rect rect, Paint paint,
{TextDirection? textDirection}) {
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect), paint);
}
@override
bool get preferPaintInterior => true;
/// Draw a rounded rectangle around [rect] using [borderRadius].
///
/// The [borderSide] defines the line's color and weight.
@override
void paint(
Canvas canvas,
Rect rect, {
double? gapStart,
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection? textDirection,
}) {
final Paint paint = borderSide.toPaint();
final RRect outer = borderRadius.toRRect(rect);
final RRect center = outer.deflate(borderSide.width / 2.0);
canvas.drawRRect(center, paint);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is OutlinedInputBorder &&
other.borderSide == borderSide &&
other.borderRadius == borderRadius;
}
@override
int get hashCode => Object.hash(borderSide, borderRadius);
}
abstract class MaterialStateOutlinedInputBorder extends OutlinedInputBorder
implements WidgetStateProperty<InputBorder> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateOutlinedInputBorder();
/// Creates a [MaterialStateOutlinedInputBorder] from a [WidgetPropertyResolver<InputBorder>]
/// callback function.
///
/// If used as a regular input border, the border resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
static MaterialStateOutlinedInputBorder resolveWith(
WidgetPropertyResolver<InputBorder> callback) =>
_MaterialStateOutlinedInputBorder(callback);
/// Returns a [InputBorder] that's to be used when a Material component is in the
/// specified state.
@override
InputBorder resolve(Set<WidgetState> states);
}
/// A [MaterialStateOutlinedInputBorder] created from a [WidgetPropertyResolver<OutlinedInputBorder>]
/// callback alone.
///
/// If used as a regular input border, the border resolved in the default state will
/// be used.
///
/// Used by [WidgetStateTextStyle.resolveWith].
class _MaterialStateOutlinedInputBorder
extends MaterialStateOutlinedInputBorder {
const _MaterialStateOutlinedInputBorder(this._resolve);
final WidgetPropertyResolver<InputBorder> _resolve;
@override
InputBorder resolve(Set<WidgetState> states) => _resolve(states);
}
@Alexi-Zemcov
Copy link

awesome 👍

@nikorehnback
Copy link

Love it ❤️

@86chenjie
Copy link

save me lots of work. thanks.

@gasaichandesu
Copy link

Thanks a lot 👍

@NiranjanShah
Copy link

can you please guide in terms of usage (how to use it).

tried below in ThemeData

inputDecorationTheme: const InputDecorationTheme(border: CustomOutlinedInputBorder()),

have named it as CustomOutlinedInputBorder.
doing so , below error comes

custom_input_border.dart:158:16: Error: Type 'WidgetStateProperty' not found.
implements WidgetStateProperty {
^^^^^^^^^^^^^^^^^^^

@Vanchel
Copy link
Author

Vanchel commented Jun 10, 2024

can you please guide in terms of usage (how to use it).

tried below in ThemeData

inputDecorationTheme: const InputDecorationTheme(border: CustomOutlinedInputBorder()),

have named it as CustomOutlinedInputBorder. doing so , below error comes

custom_input_border.dart:158:16: Error: Type 'WidgetStateProperty' not found. implements WidgetStateProperty { ^^^^^^^^^^^^^^^^^^^

In Flutter 3.22 the interfaces associated with widget state have been renamed (more). If your project uses Flutter version prior to 3.22, just use the previous revision of this gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment