I want to make a shape as seen in the photo below with container in Flutter.
How can that shape be made?
import 'dart:ui' as ui show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Card(
shape: RoundedCustomRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: const BorderSide(color: Colors.red)),
child: const SizedBox(
width: 100,
height: 100,
),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class RoundedCustomRectangleBorder extends OutlinedBorder {
/// Creates a rounded rectangle border.
///
/// The arguments must not be null.
const RoundedCustomRectangleBorder({
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : super(side: side);
/// The radii for each corner.
final BorderRadiusGeometry borderRadius;
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(side.width);
}
@override
ShapeBorder scale(double t) {
return RoundedRectangleBorder(
side: side.scale(t),
borderRadius: borderRadius * t,
);
}
@override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
if (a is RoundedRectangleBorder) {
return RoundedRectangleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius:
BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
);
}
if (a is CircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius: borderRadius,
circleness: 1.0 - t,
);
}
return super.lerpFrom(a, t);
}
@override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
if (b is RoundedRectangleBorder) {
return RoundedRectangleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius:
BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
);
}
if (b is CircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius: borderRadius,
circleness: t,
);
}
return super.lerpTo(b, t);
}
/// Returns a copy of this RoundedRectangleBorder with the given fields
/// replaced with the new values.
@override
RoundedRectangleBorder copyWith(
{BorderSide? side, BorderRadiusGeometry? borderRadius}) {
return RoundedRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..addRRect(borderRadius
.resolve(textDirection)
.toRRect(rect)
.deflate(side.width));
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
final double width = side.width;
if (width == 0.0) {
canvas.drawRRect(borderRadius.resolve(textDirection).toRRect(rect),
side.toPaint());
} else {
final RRect outer = borderRadius.resolve(textDirection).toRRect(rect);
final RRect inner = outer.deflate(width);
final Paint paint = Paint()..color = side.color;
final Paint paint2 = Paint()..color = Colors.white;
canvas.drawDRRect(outer, inner, paint);
canvas.drawPath(
Path()..addPath(getTrianglePath(40, 40), const Offset(-30, -19)),
paint);
canvas.drawPath(
Path()
..addPath(gettickpath(const Size(35, 35)), const Offset(79, 0)),
paint2);
}
}
}
Path getTrianglePath(double x, double y) {
return Path()
..moveTo(x * 2.240499, y * 0.4641165)
..lineTo(x * 3.039413, y * 0.4641165)
..arcToPoint(Offset(x * 3.240499, y * 0.6652069),
radius: Radius.elliptical(x * 0.2010859, y * 0.2010904),
rotation: 0,
largeArc: false,
clockwise: true)
..lineTo(x * 3.240499, y * 1.464117)
..close();
}
Path gettickpath(Size size) {
Path path_0 = Path();
path_0.moveTo(size.width * 0.1513658, size.height * 0.5394244);
path_0.lineTo(size.width * 0.006621293, size.height * 0.3682648);
path_0.arcToPoint(Offset(size.width * 0.006621293, size.height * 0.3311515),
radius: Radius.elliptical(
size.width * 0.02537649, size.height * 0.02780121),
rotation: 0,
largeArc: false,
clockwise: true);
path_0.lineTo(size.width * 0.03812633, size.height * 0.2940383);
path_0.arcToPoint(Offset(size.width * 0.06963136, size.height * 0.2940383),
radius: Radius.elliptical(
size.width * 0.02115734, size.height * 0.02317892),
rotation: 0,
largeArc: false,
clockwise: true);
path_0.lineTo(size.width * 0.1671953, size.height * 0.4093256);
path_0.lineTo(size.width * 0.3761818, size.height * 0.1623874);
path_0.arcToPoint(Offset(size.width * 0.4076869, size.height * 0.1623874),
radius: Radius.elliptical(
size.width * 0.02115734, size.height * 0.02317892),
rotation: 0,
largeArc: false,
clockwise: true);
path_0.lineTo(size.width * 0.4391919, size.height * 0.1995007);
path_0.arcToPoint(Offset(size.width * 0.4391919, size.height * 0.2366139),
radius: Radius.elliptical(
size.width * 0.02537649, size.height * 0.02780121),
rotation: 0,
largeArc: false,
clockwise: true);
path_0.lineTo(size.width * 0.1828709, size.height * 0.5394244);
path_0.arcToPoint(Offset(size.width * 0.1513658, size.height * 0.5394244),
radius: Radius.elliptical(
size.width * 0.02115734, size.height * 0.02317892),
rotation: 0,
largeArc: false,
clockwise: true);
path_0.close();
return path_0;
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is RoundedRectangleBorder &&
other.side == side &&
other.borderRadius == borderRadius;
}
@override
int get hashCode => Object.hash(side, borderRadius);
@override
String toString() {
return '${objectRuntimeType(this, 'RoundedRectangleBorder')}($side, $borderRadius)';
}
}
class _RoundedRectangleCustomToCircleBorder extends OutlinedBorder {
const _RoundedRectangleCustomToCircleBorder({
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
required this.circleness,
}) : super(side: side);
final BorderRadiusGeometry borderRadius;
final double circleness;
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(side.width);
}
@override
ShapeBorder scale(double t) {
return _RoundedRectangleCustomToCircleBorder(
side: side.scale(t),
borderRadius: borderRadius * t,
circleness: t,
);
}
@override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
if (a is RoundedRectangleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius:
BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
circleness: circleness * t,
);
}
if (a is CircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius: borderRadius,
circleness: circleness + (1.0 - circleness) * (1.0 - t),
);
}
if (a is _RoundedRectangleCustomToCircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius:
BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
circleness: ui.lerpDouble(a.circleness, circleness, t)!,
);
}
return super.lerpFrom(a, t);
}
@override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
if (b is RoundedRectangleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius:
BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
circleness: circleness * (1.0 - t),
);
}
if (b is CircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius: borderRadius,
circleness: circleness + (1.0 - circleness) * t,
);
}
if (b is _RoundedRectangleCustomToCircleBorder) {
return _RoundedRectangleCustomToCircleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius:
BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
circleness: ui.lerpDouble(circleness, b.circleness, t)!,
);
}
return super.lerpTo(b, t);
}
Rect _adjustRect(Rect rect) {
if (circleness == 0.0 || rect.width == rect.height) {
return rect;
}
if (rect.width < rect.height) {
final double delta = circleness * (rect.height - rect.width) / 2.0;
return Rect.fromLTRB(
rect.left,
rect.top + delta,
rect.right,
rect.bottom - delta,
);
} else {
final double delta = circleness * (rect.width - rect.height) / 2.0;
return Rect.fromLTRB(
rect.left + delta,
rect.top,
rect.right - delta,
rect.bottom,
);
}
}
BorderRadius? _adjustBorderRadius(Rect rect, TextDirection? textDirection) {
final BorderRadius resolvedRadius = borderRadius.resolve(textDirection);
if (circleness == 0.0) {
return resolvedRadius;
}
return BorderRadius.lerp(resolvedRadius,
BorderRadius.circular(rect.shortestSide / 2.0), circleness);
}
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..addRRect(_adjustBorderRadius(rect, textDirection)!
.toRRect(_adjustRect(rect))
.deflate(side.width));
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()
..addRRect(
_adjustBorderRadius(rect, textDirection)!.toRRect(_adjustRect(rect)));
}
@override
_RoundedRectangleCustomToCircleBorder copyWith(
{BorderSide? side,
BorderRadiusGeometry? borderRadius,
double? circleness}) {
return _RoundedRectangleCustomToCircleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
circleness: circleness ?? this.circleness,
);
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
final double width = side.width;
if (width == 0.0) {
canvas.drawRRect(
_adjustBorderRadius(rect, textDirection)!
.toRRect(_adjustRect(rect)),
side.toPaint());
} else {
final RRect outer = _adjustBorderRadius(rect, textDirection)!
.toRRect(_adjustRect(rect));
final RRect inner = outer.deflate(width);
final Paint paint = Paint()..color = side.color;
canvas.drawDRRect(outer, inner, paint);
}
}
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is _RoundedRectangleCustomToCircleBorder &&
other.side == side &&
other.borderRadius == borderRadius &&
other.circleness == circleness;
}
@override
int get hashCode => Object.hash(side, borderRadius, circleness);
@override
String toString() {
return 'RoundedRectangleBorder($side, $borderRadius, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder)';
}
}