The z-order of stack children layout is not as my expectation.
I have 4 widgets in a stack with keys: 0,1,2,3. I try to change their z-order and rebuild the stack widget. Here is my code:
class TestBringToFrontPage extends StatefulWidget {
TestBringToFrontPage({Key key}) : super(key: key);
#override
State<TestBringToFrontPage> createState() => TestBringToFrontState();
}
class TestBringToFrontState extends State<TestBringToFrontPage> {
List<Widget> _widgets = [];
#override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
..._widgets,
Positioned(
bottom: 0,
right: 0,
child: RaisedButton(
child: Text(
"click",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
_swap(0, 2);
_swap(1, 3);
print("$_widgets");
});
},
)),
]);
}
#override
void initState() {
super.initState();
const double start = 100;
const double size = 100;
_widgets = <Widget>[
_buildWidget(key: const Key("0"), color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
_buildWidget(key: const Key("1"), color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
_buildWidget(key: const Key("2"), color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
_buildWidget(key: const Key("3"), color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
];
}
Widget _buildWidget({Key key, Color color, Offset offset, Size size}) {
final label = (key as ValueKey<String>)?.value;
return Positioned(
key: key,
left: 0,
top: 0,
child: Container(
transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
width: size.width,
height: size.height,
decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
child: Text(
label,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
decoration: TextDecoration.none,
fontSize: 15.0,
),
),
));
}
void _swap(int x, int y) {
final w = _widgets[x];
_widgets[x] = _widgets[y];
_widgets[y] = w;
}
}
At starting, stack children are laid out in z-order (from bottom to top): 0,1,2,3. Screen shows:
Starting screen state image
By clicking to the button, I expect the stack children are laid out in new z-order: 2,3,0,1. And screen should show:
Expected screen state image
But not as expected, screen shows:
Actual screen state image.
Console log when clicking the button: "[Positioned-[<'2'>](left: 0.0, top: 0.0), Positioned-[<'3'>](left: 0.0, top: 0.0), Positioned-[<'0'>](left: 0.0, top: 0.0), Positioned-[<'1'>](left: 0.0, top: 0.0)]"
In Flutter inspectors window, the stack children are in correct order: 2,3,0,1. But stack renders wrong.
Do you know where am I wrong or is it flutter layout issue?
Thank you in advance,
After playing around with your code for a little while, I've managed to figure out that the Key on each block is holding some state that messes their ordering up during setState. Removing the keys gives me the behaviour you're expecting. I don't know why this would be happening, but at least now you have a fix if the keys aren't needed for whatever your goal is.
Whether they are or not, I would file a bug report if I were you, because I'm pretty sure this behaviour isn't intended.
Here's the code I used that works (I cleaned up a little bit of it so it doesn't override initState):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: TestBringToFrontPage(),
);
}
}
class TestBringToFrontPage extends StatefulWidget {
TestBringToFrontPage({Key key}) : super(key: key);
#override
State<TestBringToFrontPage> createState() => TestBringToFrontState();
}
class TestBringToFrontState extends State<TestBringToFrontPage> {
static const start = 50.0;
static const size = 250.0;
final _widgets = <Widget>[
_buildWidget(label: "0", color: Colors.blue, offset: const Offset(start, start), size: const Size(size, size)),
_buildWidget(label: "1", color: Colors.green, offset: const Offset(start + size / 2, start), size: const Size(size, size)),
_buildWidget(label: "2", color: Colors.yellow, offset: const Offset(start, start + size / 2), size: const Size(size, size)),
_buildWidget(label: "3", color: Colors.red, offset: const Offset(start + size / 2, start + size / 2), size: const Size(size, size)),
];
#override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
..._widgets,
Positioned(
bottom: 0,
right: 0,
child: RaisedButton(
child: Text(
"click",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
_swap(0, 2);
_swap(1, 3);
print("$_widgets");
});
},
)),
]);
}
static Widget _buildWidget({String label, Color color, Offset offset, Size size}) {
return Positioned(
left: 0,
top: 0,
child: Container(
transform: Matrix4.identity()..translate(offset.dx, offset.dy, 0),
width: size.width,
height: size.height,
decoration: BoxDecoration(border: Border.all(color: Colors.black, width: 1.0), color: color),
child: Text(
label,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w700,
decoration: TextDecoration.none,
fontSize: 30.0,
),
),
));
}
void _swap(int x, int y) {
final w = _widgets[x];
_widgets[x] = _widgets[y];
_widgets[y] = w;
}
}
Related
I would like to know how to create a slider to change the text size.
This is the design.
あ = A
i want increase +0.05
My code is bad . because i can not made same design .
CupertinoSlider(
min: 0.1,
max: 0.6,
value: clockTextSize.toDouble(),
onChanged: (_value) {
setState(
() {
clockTextSize = _value.toInt();
},
);
},
)
i want increase +0.05
you can set division property to achieve this. see documentation
For example, if min is 0.1 and max is 0.6 and divisions is 5, then the slider can take on the values discrete values 0.1, 0.2, 0.3, 0.4, 0.5, and 0.6.
then in your case, if you want increase +0.05, you can set the division=10
CupertinoSlider(
min: 0.1,
max: 0.6,
division:10,
...
result:
Note: your question wasn't too clear if you're looking to achieve the same design or regarding the divisions. This answer is how to achieve the same design.
Result
Customization
To customize the Slider to your liking, you can use SliderTheme.
Then, to customize the Tick, you can create your own class that extends SliderTickMarkShape.
Code
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(debugShowCheckedModeBanner: false, home: Test()));
}
class Test extends StatefulWidget {
const Test({Key? key}) : super(key: key);
#override
State<Test> createState() => _TestState();
}
class _TestState extends State<Test> {
var value = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 200,
width: 500,
child: Card(
margin: const EdgeInsets.all(10),
color: const Color(0xff323237),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"あ",
style: TextStyle(color: Colors.grey),
),
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
tickMarkShape: CustomTick(),
trackHeight: 10.0,
minThumbSeparation: 2,
thumbColor: Colors.white,
thumbShape: const RoundSliderThumbShape(
enabledThumbRadius: 20,
),
inactiveTickMarkColor: Colors.grey,
inactiveTrackColor: Colors.grey,
activeTrackColor: Colors.grey,
),
child: Slider(
min: 1,
max: 100,
value: value.toDouble(),
divisions: 12,
onChanged: (newValue) {
setState(() {
value = newValue.round();
});
},
),
),
),
const Text(
"あ",
style: TextStyle(color: Colors.grey),
),
],
),
),
),
),
),
);
}
}
class CustomTick extends SliderTickMarkShape {
#override
Size getPreferredSize(
{required SliderThemeData sliderTheme, required bool isEnabled}) {
return const Size(10, 10);
}
#override
void paint(PaintingContext context, Offset center,
{required RenderBox parentBox,
required SliderThemeData sliderTheme,
required Animation<double> enableAnimation,
required Offset thumbCenter,
required bool isEnabled,
required TextDirection textDirection}) {
final Canvas canvas = context.canvas;
final Paint paint = Paint()..color = Colors.grey;
canvas.drawRect(
Rect.fromCenter(center: center, width: 5, height: 20), paint);
}
}
See also
How can I customize Slider widget in Flutter?
At the end of liquid swipe, when the Let's start button is clicked, I would like confetti animation to play for 3-4 seconds and then present the user with App home screen. When I run this code, I don't get any error and neither does confetti animation play. On clicking Let's start, the home screen of app is displayed.
Update: 25/11/2022 I was able to run confetti successfully and have also added a listener to capture current state. I would like the confetti to run for 3 seconds and the screen to be routed to home screen using
AppRoute.pushReplacement(context, const MyApp());
import 'package:a/main.dart';
import 'package:a/provider/home_provider.dart';
import 'package:a/provider/user_provider.dart';
import 'package:a/ui/app_route.dart';
import 'package:a/ui/widget/styled_text.dart';
import 'package:a/utils/app_colors.dart';
import 'package:flutter/material.dart';
import 'package:liquid_swipe/liquid_swipe.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:confetti/confetti.dart';
class OnBoarding extends StatefulWidget {
const OnBoarding({Key? key}) : super(key: key);
#override
_OnBoardingState createState() => _OnBoardingState();
}
class _OnBoardingState extends State<OnBoarding> {
#override
Widget build(BuildContext context) {
final pages = [
IntroductionContainer(
heading:
'a',
body:
"b",
color: Color(0xff3937bf),
animation: 'assets/lottie/a.json',
),
IntroductionContainer(
heading:
'c?',
body:
"d",
color: Colors.pink,
animation: 'assets/lottie/h.json',
),
IntroductionContainer(
heading:
'e',
body:
"f",
color: Color(0xff27b56f),
animation: 'assets/lottie/g.json',
),
IntroductionContainer(
showAction: true,
heading: 'Ready ?',
body:
"aaaa",
color: Color(0xfff46d37),
),
];
return Scaffold(
body: LiquidSwipe(
pages: pages,
enableSideReveal: true,
// enableSlideIcon: true,
enableLoop: false,
positionSlideIcon: 0,
slideIconWidget: const Icon(
Icons.arrow_back_ios,
size: 30,
color: Colors.white,
),
),
);
}
}
class IntroductionContainer extends StatefulWidget {
IntroductionContainer(
{required this.heading,
required this.body,
required this.color,
this.showAction = false,
this.animation,
Key? key})
: super(key: key);
final String heading;
final String body;
final Color color;
final bool showAction;
final String? animation;
#override
State<IntroductionContainer> createState() => _IntroductionContainerState();
}
class _IntroductionContainerState extends State<IntroductionContainer> {
//Adding Confetti controller and other variables
bool isPlaying = false;
final _controller = ConfettiController(duration: const Duration(seconds: 2));
#override
void initState() {
// TODO: implement initState
super.initState();
//Listen to states playing, stopped
_controller.addListener(() {
setState(() {
isPlaying = _controller.state == ConfettiControllerState.playing;
});
if (_controller.state == ConfettiControllerState.stopped) {
AppRoute.pushReplacement(context, const MyApp());
}
});
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 25,
),
),
const SizedBox(height: 10),
if (widget.animation != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Lottie.asset(
widget.animation!,
height: 400,
width: 400,
fit: BoxFit.fill,
),
),
if (widget.animation == null)
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Stack(
alignment: Alignment.center,
children: [
Container(
height: 200,
width: 200,
alignment: Alignment.center,
child: Image.asset(
'assets/a-2.png',
height: 120,
width: 120,
fit: BoxFit.contain,
),
),
const Icon(
Icons.play_circle,
size: 60,
)
],
),
),
onTap: () async {
var url = 'https://www.youtube.com/watch?v=aaaaaa';
if (await canLaunchUrlString(url)) {
launchUrlString(url);
}
},
),
Text(
widget.body,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
),
),
ConfettiWidget(
confettiController: _controller,
//set direction
//blastDirectionality: BlastDirectionality.explosive,
// to set direction of confetti upwards
blastDirection: -3.14 / 2,
//minBlastForce: 10,
//maxBlastForce: 100,
//colors: const [Colors.deepPurple, Colors.black, Colors.yellow],
numberOfParticles: 20,
gravity: 0.5,
emissionFrequency: 0.3,
// - Path to create oval particles
createParticlePath: (size) {
final path = Path();
path.addOval(Rect.fromCircle(
center: Offset.zero,
radius: 4,
));
return path;
},
// Path to create star confetti
/// A custom Path to paint stars.
//Path drawStar(Size size) {
// Method to convert degree to radians
//double degToRad(double deg) => deg * (pi / 180.0);
//const numberOfPoints = 5;
//final halfWidth = size.width / 2;
//final externalRadius = halfWidth;
//final internalRadius = halfWidth / 2.5;
//final degreesPerStep = degToRad(360 / numberOfPoints);
//final halfDegreesPerStep = degreesPerStep / 2;
//final path = Path();
//final fullAngle = degToRad(360);
//path.moveTo(size.width, halfWidth);
//for (double step = 0; step < fullAngle; step += degreesPerStep) {
//path.lineTo(halfWidth + externalRadius * cos(step),
// halfWidth + externalRadius * sin(step));
//path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep),
// halfWidth + internalRadius * sin(step + halfDegreesPerStep));
// }
//path.close();
//return path;
//}
),
//ConfettiWidget(
const SizedBox(height: 50),
if (widget.showAction)
ButtonTheme(
height: 50,
minWidth: 150,
child: MaterialButton(
// borderSide: BorderSide(color: Colors.white),
color: AppColors.blue,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'Let\'s Go',
style: TextStyle(fontSize: 20),
),
onPressed: () async {
//Calling Confetti controller and adding confetti widget
if (isPlaying) {
_controller.stop();
//await Duration(seconds: 4);
AppRoute.pushReplacement(context, const MyApp());
} else {
_controller.play();
//await Duration(seconds: 1);
}
//await Duration(seconds: 1);
//confettiController: confettiController,
// shouldLoop: false,
// blastDirectionality: BlastDirectionality.explosive,
//);
//var userProvider =
//Provider.of<UserProvider>(context, listen: false);
//userProvider.updateTrial();
// HomeProvider.setAppLaunched();
//AppRoute.pushReplacement(context, const MyApp());
//},
}),
),
],
),
),
);
}
}
At the end of liquid swipe, when the Let's start button is clicked, I would like confetti animation to play for 3-4 seconds and then present the user with App home screen. When I run this code, I don't get any error and neither does confetti animation play. On clicking Let's start, the home screen of app is displayed.
PS: I am new to flutter
Your mistake is that you put the ConfettiWidget inside of the onPressed callback of your button.
You should add it in your Column like any other widget and display the confetti by triggering the controller you pass to your ConfettiWidget inside the onPressed callback with the help of _controller.play()
I've been trying to implement a similar function like the NavigationBar widget in flutter.
However, I don't want to use Icons instead I wanted to make a custom navbar with desired pics and everything was going well until I couldn't switch the middle section of my app (change different pages) when I tap/press the the textbutton.
You can check the UI here...crappy I know...am mimicking the till from my workplace...so the red section is the part I wanted to update when pressed
The side_buttons.dart file
import 'package:flutter/material.dart';
// ignore: unused_import
import 'package:timtill/main.dart';
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
const SideButtons({required this.text, required this.imgUrl});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int currentPageIndex = 0;
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
'''
The Main.dart file
Note I wanted to update the currentPageIndex value from zero to the index number When I press the buttons please help me I'm beginner
import 'package:flutter/material.dart';
import 'package:timtill/pages/side_btn_page.dart';
import 'package:timtill/pages/middle_btn_page.dart';
import 'package:timtill/pages/middle_btn_page2.dart';
// ignore: unused_import
import 'package:timtill/util/side_buttons.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TimsTill',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentPageIndex = 0;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: [
SizedBox(height: 80, child: SideButtonPage()),
Expanded(
flex: 12,
child: Container(
color: Colors.red,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: <Widget>[
MiddleButtonPage(),
MiddleButtonPage2(),
Container(
color: Colors.green,
alignment: Alignment.center,
child: const Text('Page 2'),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
child: const Text('Page 3'),
),
][currentPageIndex],
),
),
),
),
)),
Expanded(
flex: 6,
child: Container(
color: Colors.purple,
))
],
),
);
}
}
First of all, you should implement a callback in your SideButtons widget, second, you should implement the defaultPageIndex. This way, SideButtons will return the selected index to its parent widget while maintening its state incase the widget try is rebuilt.
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
final int defaultPageIndex;
final ValueChanged<int>? onChanged;
const SideButtons({required this.text, required this.imgUrl, this.defaultPageIndex = 0, this.onChanged});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
late int currentPageIndex;
#override
initState(){
currentPageIndex = defaultPageIndex;
super.initState();
}
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
if( widget.onChanged != null) widget.onChanged(index);
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
How Can I create this curves and two coloured top "appBar" using Flutter?
The CustomPaint widget will do the trick. With it, it's possible to paint custom shapes in the background like the one you asked for. It's just a matter of using the Stack widget to paint the background first and then the other widgets above it.
This is a prototype of the login screen:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
const loginMainColor = Color.fromARGB(255, 67, 123, 122);
const loginOtherColor = Color.fromARGB(255, 253, 236, 229);
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom Shape Widget Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const LoginPage(),
);
}
}
class LoginPage extends StatelessWidget {
const LoginPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: loginMainColor,
),
backgroundColor: Colors.white,
body: Stack(
children: [
Stack(
children: [
CustomPaint(
size: Size(width, height),
painter: const BackgroundPainter(90),
),
Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'Log in',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.white),
),
Padding(
padding: EdgeInsets.only(bottom: 60 - 16),
child: Text(
'Lorem ipsum dolor sit amet',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
Padding(
padding: EdgeInsets.only(bottom: 30),
child: TextField(
decoration: InputDecoration(
label: Text('Email'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
),
TextField(
decoration: InputDecoration(
label: Text('Password'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
)
],
),
),
],
),
],
),
);
}
}
class BackgroundPainter extends CustomPainter {
final double titleBarHeight;
const BackgroundPainter(this.titleBarHeight);
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = loginMainColor;
const smallRadius = 50.0;
const bigRadius = 120.0;
canvas.drawCircle(
Offset(smallRadius, titleBarHeight - smallRadius), smallRadius, paint);
canvas.drawRect(
Rect.fromPoints(
Offset(0, 0),
Offset(size.width, titleBarHeight - smallRadius),
),
paint,
);
canvas.drawRect(
Rect.fromPoints(
Offset(smallRadius, titleBarHeight - smallRadius),
Offset(size.width, titleBarHeight),
),
paint,
);
paint.color = loginOtherColor;
canvas.drawCircle(
Offset(size.width, titleBarHeight + 60), bigRadius, paint);
paint.color = Colors.white;
canvas.drawCircle(
Offset(size.width - smallRadius, titleBarHeight + smallRadius),
smallRadius,
paint);
canvas.drawRect(
Rect.fromPoints(
Offset(size.width - bigRadius, titleBarHeight),
Offset(size.width - smallRadius, titleBarHeight + 60 + bigRadius),
),
paint);
canvas.drawRect(
Rect.fromPoints(
Offset(size.width - smallRadius, titleBarHeight + smallRadius),
Offset(size.width, titleBarHeight + 60 + bigRadius),
),
paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
As lepsch commented, CustomPaint is the way to go, but it you find it complicated or time-consuming, you can also get some help from tools such as FlutterShapeMaker (FlutterShapeMaker), which allows you to draw your shapes as if you were in some design software and export them into a CustomPainter class
First you edit your shape as you like
Then you export it and the tool generates the required class and imports
I want to create toggle buttons and evenly space each element in the list of toggle buttons and make each selected button rounded like this,
I've tried using boxconstraints property, width property, margin property and the rest,
But this is what I'm getting, I've tried every other thing but I can't get it, this is what I'm getting
This is the code I'm using
import 'package:flutter/material.dart';
class TestingScreen extends StatefulWidget {
#override
State<TestingScreen> createState() => _TestingScreenState();
}
class _TestingScreenState extends State<TestingScreen> {
List<bool> _isSelected = [true, false, false, false];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 100),
child: Row(
children: [
Text(
'Time',
style: TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
ToggleButtons(
color: Color(0xff001666),
fillColor: Color(0xff001666),
selectedColor: Colors.white,
children: [
ToggleButton(name: '1D'),
ToggleButton(name: '1W'),
ToggleButton(name: '1M'),
ToggleButton(name: '1Y'),
],
isSelected: _isSelected,
onPressed: (int newIndex) {
setState(() {
for (int i = 0; i < _isSelected.length; i++) {
if (i == newIndex) {
_isSelected[i] = true;
} else {
_isSelected[i] = false;
}
print(_isSelected);
}
});
},
)
],
),
),
);
}
}
class ToggleButton extends StatelessWidget {
final String name;
const ToggleButton({Key? key, required this.name}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width * 0.1,
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),
padding: EdgeInsets.symmetric(vertical: 4),
alignment: Alignment.center,
child: Text(
name,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w400,
),
),
);
}
}
You can add renderBorder: false, property to remove the ash colored border and borderRadius: BorderRadius.circular(15), to make the round circled border in the outside and make the shape you can use constraints: const BoxConstraints.expand(height: 25,width: 34), to get the the exact size of the height and the width.enter image description here
but to get the exact result you have to use Inkwell() or ElvatedButton() or IconButton() bcz there isnt any property to use the borderRadius: BorderRadius.circular(15), for the each of the icons in the buttons as showed in the picture.
Hope it will work for you.