I want to ask about design in flutter. I don't know how to do it.
I insert the image for your see my draw. Hope your guys can help me.
For Creating Horizontal line(Divider) around Text Widget
Row(children: <Widget>[
Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 10.0, right: 20.0),
child: Divider(
color: Colors.black,
height: 36,
)),
),
Text("OR"),
Expanded(
child: new Container(
margin: const EdgeInsets.only(left: 20.0, right: 10.0),
child: Divider(
color: Colors.black,
height: 36,
)),
),
]),
Try this class,
class DividerWithText extends StatelessWidget {
final String text;
final TextStyle textStyle;
final Color color;
const DividerWithText({Key key, this.text, this.color, this.textStyle})
: assert(text != null),
super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(child: Divider(color: color)),
const SizedBox(width: 5.0),
Text(
text,
style: textStyle?.copyWith(color: textStyle.color ?? color) ??
TextStyle(color: color),
),
const SizedBox(width: 5.0),
Expanded(child: Divider(color: color)),
],
);
}
}
Usage:
Text("Login"),
DividerWithText(
text: "Or",
textStyle: TextStyle(fontSize: 20.0),
color: Colors.red,
),
Text("Google"),
there is lots of approach to this
1) using something like this
Row(
children: <Widget>[
Flexible(
child: Divider(
color: Colors.black,
height: 4,
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Text(
"Or",
style: TextStyle(backgroundColor: Colors.white),
),
),
Flexible(
child: Divider(
color: Colors.black,
height: 4,
),
),
],
)
2) this code has better performance
class TextWithLine extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomPaint(
size: Size(MediaQuery.of(context).size.width, 100),
painter: TextWithLinePainter(
color: Colors.red,
text: "Or",
textStyle: TextStyle(
color: Colors.black,
fontSize: 15,
),
lineColor: Colors.black54,
lineSize: 1,
margin: 8,
),
);
}
}
class TextWithLinePainter extends CustomPainter {
String text;
TextStyle textStyle;
Color color;
Color lineColor;
double lineSize;
double margin;
TextWithLinePainter({this.text, this.textStyle, this.color, this.lineColor, this.lineSize, this.margin});
#override
void paint(Canvas canvas, Size size) {
final textSpan = TextSpan(
text: text,
style: textStyle,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.textAlign = TextAlign.center;
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
var textW = textPainter.width;
var textH = textPainter.height;
textPainter.paint(
canvas,
Offset(
size.width / 2 - textW / 2,
size.height / 2 - textH / 2,
),
);
Paint linePainter = Paint()
..color = lineColor
..isAntiAlias = true
..strokeWidth = lineSize;
var firstLineEndX = size.width / 2 - (textW / 2) - margin;
canvas.drawLine(
Offset(0, size.height / 2),
Offset(firstLineEndX, size.height / 2),
linePainter,
);
var secondLineStartX = (size.width / 2) + (textW / 2) + margin;
canvas.drawLine(
Offset(secondLineStartX, size.height / 2),
Offset(size.width, size.height / 2),
linePainter,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
Related
Need to display icon with checkmark based on a String value that comes dynamically.
Like
this image is its pending show first widget with tick and rest are blank.
if delivered show with tick and the rest are blank.
Facing problems in creating logic using enums.
Currently, it displays the icons on button clicks
based on four constants which is fine with the widget CheckStatus.
Need to make in a way based on a boolean check if it's true and pending that pending tick widget displayed
and similar with other values.
Here is the complete code for it currently.
import 'package:dotted_border/dotted_border.dart';
import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:matab/models/order.dart';
import 'package:matab/ui/general_widgets/check_status.dart';
import 'package:matab/ui/pages/styles.dart';
import '../../general_widgets/custom_gradient_button.dart';
class TrackOrder extends StatefulWidget {
const TrackOrder({Key? key, required this.order}) : super(key: key);
final Order order;
#override
State<TrackOrder> createState() => _TrackOrderState();
}
enum Status { Pending, Confirmed, Shipped, Received }
class _TrackOrderState extends State<TrackOrder> {
static const darkGreyColor = Colors.grey;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text('Track Order')),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 50),
Text(
"Order ID:" + widget.order.orderID,
style: const TextStyle(
color: darkGreyColor,
fontSize: 18,
fontWeight: FontWeight.bold),
),
const SizedBox(height: 50),
const Text('Sat, 12 Mar 2022',
style: TextStyle(
color: darkGreyColor,
fontSize: 18,
fontWeight: FontWeight.bold)),
const SizedBox(
height: 15,
),
Container(
margin: const EdgeInsets.fromLTRB(15, 0, 0, 0),
child: const Text('Estimated Time: 07 Days',
style: TextStyle(fontSize: 23, fontWeight: FontWeight.bold)),
),
const SizedBox(height: 30),
SizedBox(
width: 200,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
OrderStatusBar(title: widget.order.orderStatus, status: true),
dottedLine(),
OrderStatusBar(
title: widget.order.orderStatus, status: false),
dottedLine(),
OrderStatusBar(
title: widget.order.orderStatus, status: false),
dottedLine(),
OrderStatusBar(
title: widget.order.orderStatus, status: false),
],
),
),
const SizedBox(
height: 40,
),
Container(
margin: const EdgeInsets.fromLTRB(15, 0, 0, 0),
child: const Text('Shipping Address',
style: TextStyle(fontSize: 23, fontWeight: FontWeight.bold)),
),
Center(
child: Text(widget.order.deliveryAddress.address,
style: const TextStyle(
color: Colors.grey,
fontSize: 18,
fontWeight: FontWeight.bold)),
),
Center(
child: Padding(
padding: const EdgeInsets.all(
50.0,
),
child: CustomGradientButton(
buttonText: "Track Order".tr, buttonFunction: () => {}),
),
),
Center(
child: Padding(
padding: const EdgeInsets.only(top: 18.0),
child: GestureDetector(
child: Text(
'Back to Home'.tr,
style: TextStyle(
color: mainColor,
fontSize: 23,
fontWeight: FontWeight.bold),
),
onTap: () => {
Get.off(CheckStatus(
order: widget.order,
))
},
),
),
)
],
),
),
);
}
}
class OrderStatusBar extends StatefulWidget {
const OrderStatusBar({Key? key, required this.title, required this.status})
: super(key: key);
final String title;
final bool status;
#override
State<OrderStatusBar> createState() => _OrderStatusBarState();
}
class _OrderStatusBarState extends State<OrderStatusBar> {
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Row(
children: [
widget.status ? dottedCircleWithCheckMark() : dottedCircle(),
const SizedBox(width: 30),
Text(
widget.title.tr,
style: TextStyle(
fontSize: 20,
fontWeight: widget.status ? FontWeight.bold : null,
),
),
],
),
);
}
}
const size = 25.0;
const strokeWidth = 1.0;
const checkedColor = Color.fromRGBO(232, 113, 65, 1);
Widget dottedLine() {
return Directionality(
textDirection: TextDirection.rtl,
child: Align(
alignment: Alignment.topRight,
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, size / 2, 0),
child: const Padding(
padding: EdgeInsets.only(left: 27 / 2),
child: SizedBox(
height: size,
child: DottedLine(
dashColor: Colors.black,
direction: Axis.vertical,
lineLength: size,
lineThickness: strokeWidth,
dashLength: 5,
dashGapLength: 5,
),
),
),
),
),
);
}
dottedCircle() {
return DottedBorder(
borderType: BorderType.Circle,
dashPattern: const [5, 5],
child: Container(
height: size,
width: size,
decoration: const BoxDecoration(shape: BoxShape.circle),
));
}
dottedCircleWithCheckMark() {
return Container(
height: size + strokeWidth * 2,
width: size + strokeWidth * 2,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: checkedColor,
),
child: const Icon(
Icons.check,
color: Colors.white,
size: size / 4 * 3,
),
);
}
// ignore_for_file: constant_identifier_names
class CheckStatus extends StatefulWidget {
const CheckStatus({Key? key, required this.order}) : super(key: key);
final Order order;
#override
State<CheckStatus> createState() => _CheckStatusState();
}
class _CheckStatusState extends State<CheckStatus> {
int selectedItemIndex = 0;
var pending = Status.Pending;
List<bool> orderStatus = [true,true,true,false];
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
for (int i = 0; i < Status.values.length; i++)
ElevatedButton(
onPressed: () {
selectedItemIndex = i;
setState(() {});
},
child: Text("Order Status ${Status.values[i]}"),
),
Row(
children: [
for (int i = 0; i <= selectedItemIndex; i++)
Container(
height: size + strokeWidth * 2,
width: size + strokeWidth * 2,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: checkedColor,
),
child: const Icon(
Icons.check,
color: Colors.white,
size: size / 4 * 3,
),
),
ElevatedButton(onPressed: () {}, child: Text("Back"))
],
)
],
),
);
}
}
Can anyone explain me how to change clip path of sliver app bar in flutter
class MyClipper extends CustomClipper<Path> {
#override Path getClip(Size size) {
final radius = 32.0;
final r = Offset.zero & size;
final r1 = Rect.fromCircle(center: r.bottomRight - Offset(radius, radius * 2), radius: radius);
final r2 = Rect.fromCircle(center: r.bottomLeft + Offset(radius, 0), radius: radius);
return Path()
..moveTo(r.topLeft.dx, r.topLeft.dy)
..lineTo(r.topRight.dx, r.topRight.dy)
..arcTo(r1, 0, pi / 2, false)
..arcTo(r2, -pi / 2, -pi / 2, false);
}
#override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}
Answer owner: pskink
You can use that, my design supporting auto resize clip path and working perfectly
import 'package:flutter/material.dart';
import 'package:unfpa/base/base_stateful_state.dart';
import 'package:unfpa/utils/constants.dart';
import 'package:unfpa/utils/custom_clip_path.dart';
import 'package:unfpa/utils/custom_clip_path_two.dart';
class TestWidget extends StatefulWidget {
TestWidget({Key key}) : super(key: key);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends BaseStatefulState<TestWidget> {
final ScrollController _sliverScrollController = ScrollController();
ValueNotifier<bool> isPinned = ValueNotifier(false);
Size size;
#override
void initState() {
super.initState();
_sliverScrollController.addListener(() {
if (!isPinned.value &&
_sliverScrollController.hasClients &&
_sliverScrollController.offset > kToolbarHeight) {
isPinned.value = true;
} else if (isPinned.value &&
_sliverScrollController.hasClients &&
_sliverScrollController.offset < kToolbarHeight) {
isPinned.value = false;
}
});
}
#override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
controller: _sliverScrollController,
slivers: <Widget>[
ValueListenableBuilder(
valueListenable: isPinned,
builder: (context, value, child) {
return SliverAppBar(
elevation: 0,
backgroundColor: Colors.red,
leading: IconButton(
iconSize: 48,
color: Colors.white,
icon: Icon(Icons.close),
onPressed: () => Navigator.of(context).pop()),
primary: true,
pinned: true,
stretch: true,
title: Visibility(
visible: value,
child: Text(
"Sliver AppBar Title",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 22.0,
color: Colors.white,
),
),
),
titleSpacing: 0,
);
}),
SliverList(
delegate: SliverChildListDelegate([
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
transform: Matrix4.translationValues(0.0, -2.0, 0.0),
alignment: Alignment.center,
child: ClipPath(
clipper: ClipPathClass(),
child: Container(
padding: EdgeInsets.only(bottom:20),
width: size.width,
color: Colors.yellow,
child: Container(
margin: EdgeInsets.symmetric(
horizontal: 25),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: kToolbarTopMargin),
Text(
"Expanded Sliver title",
style: TextStyle(
fontSize: 30.0,
color: Colors.white,
),
),
SizedBox(
height: 5,
),
Text(
"Expanded sliver description",
style: TextStyle(
fontSize: 17,
color: Colors.white,
height: 1.3,
fontWeight: FontWeight.w700,
),
)
],
),
),
),
),
),
],
)
])),
],
));
}
}
CustomClipPathClass
import 'package:flutter/material.dart';
class ClipPathClass extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0.0, size.height - 30);
var firstControlPoint = Offset(size.width / 4, size.height);
var firstPoint = Offset(size.width / 2, size.height);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstPoint.dx, firstPoint.dy);
var secondControlPoint = Offset(size.width - (size.width / 4), size.height);
var secondPoint = Offset(size.width, size.height - 30);
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
secondPoint.dx, secondPoint.dy);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
I want to achieve have a round icon for the profile image like in this example:
But is not working for me, this is what I achieved so far:
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
ClipPath(
clipper: _AppBarProfileClipper(childHeight),
child: Container(
padding: const EdgeInsets.only(top: 48.0),
color: TheBaseColors.lightBlue,
height: height,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
leading,
FlatButton(
onPressed: null,
child: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18.0,
color: Colors.white,
),
),
),
trailing,
],
),
),
),
Positioned(
bottom: childHeight / 2, //50
left: 0,
right: 0,
child: Align(
alignment: Alignment.bottomCenter,
child: child,
),
),
],
);
}
}
All the parameters are represented in the constructor and used in the code. But I cant figure out how to make that nice effect with the rounded icon.
Someone knows why is not working?
You can achieve given example like this:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final double appBarHeight = 100.0;
final double spaceAroundRoundButton = 4.0;
final double roundedButtonSize = 64.0;
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return SafeArea(
child: Stack(
children: <Widget>[
Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize:
Size(MediaQuery.of(context).size.width, appBarHeight),
child: ClipPath(
clipper: BottomClipper(),
child: Container(
alignment: Alignment.topLeft,
height: appBarHeight,
color: Colors.black,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
Icons.menu,
color: Colors.white,
),
onPressed: () {}),
Text(
'User Profile',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
IconButton(
icon: Icon(
Icons.settings,
color: Colors.white,
),
onPressed: () {})
],
),
),
)),
body: Container(
alignment: Alignment.topCenter,
margin: EdgeInsets.only(
top:
roundedButtonSize * 0.75 + spaceAroundRoundButton + 16.0),
child: Text('Body'),
),
),
Positioned(
child: Container(
padding: EdgeInsets.all(spaceAroundRoundButton),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: RawMaterialButton(
elevation: 0,
constraints: BoxConstraints.tightFor(
width: roundedButtonSize, height: roundedButtonSize),
shape: StadiumBorder(
side: BorderSide(width: 4, color: Colors.grey)),
child: Icon(Icons.person),
onPressed: () {},
fillColor: Colors.white,
),
),
left: (size.width / 2) -
(roundedButtonSize / 2) -
spaceAroundRoundButton,
top: appBarHeight -
(roundedButtonSize * 0.25) -
spaceAroundRoundButton,
)
],
),
);
}
}
class BottomClipper extends CustomClipper<Path> {
#override
Path getClip(Size size) {
var path = Path();
path.lineTo(0.0, size.height * 0.75);
var firstControlPoint = Offset(size.width * 0.25, size.height);
var firstEndPoint = Offset(size.width * 0.5, size.height);
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy,
firstEndPoint.dx, firstEndPoint.dy);
var secondControlPoint = Offset(size.width * 0.75, size.height);
var secondEndPoint = Offset(size.width, size.height * 0.75);
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy,
secondEndPoint.dx, secondEndPoint.dy);
path.lineTo(size.width, 0.0);
path.close();
return path;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
Screenshot:
I'm building a page with multiple styled sliders, so I made a separate SliderWidget class. The slider itself behaves normally, but when I try to print the value of the slider to console, it returns null as flutter: null.
I've tried putting specific int values into my currentValue variable (in the SliderWidget class) to test with no luck, so I'm pretty sure that the problem is the getter itself, but I can't figure out what is wrong. I'm pretty new to flutter, so it's very possible I did something stupid.
Here is the slider object:
SliderWidget moodSlider = new SliderWidget(
sliderHeight: 48,
min: 0,
max: 10,
color1: 0xFF9BC1BC,
color2: 0xFF9BC1BC);
Here is the SliderWidget class:
import 'package:first_tutorial/MoodUpdate/custom_thumb_circle.dart';
import 'package:flutter/material.dart';
class SliderWidget extends StatefulWidget {
final double sliderHeight;
final int min;
final int max;
final fullWidth;
final int color1;
final int color2;
final int number;
SliderWidget(
{this.sliderHeight,
this.max,
this.min,
this.fullWidth = false,
this.color1,
this.color2,
this.number});
#override
_SliderWidgetState createState() => _SliderWidgetState();
}
class _SliderWidgetState extends State<SliderWidget> {
double currentValue = 0;
int get number => currentValue.round();
#override
Widget build(BuildContext context) {
double paddingFactor = .2;
if (this.widget.fullWidth) paddingFactor = .3;
return Container(
width: this.widget.fullWidth
? double.infinity
: (this.widget.sliderHeight) * 5.5,
height: (this.widget.sliderHeight),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(
Radius.circular((this.widget.sliderHeight * .3)),
),
gradient: new LinearGradient(
colors: [
Color(this.widget.color1),
Color(this.widget.color2),
],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 1.00),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: Padding(
padding: EdgeInsets.fromLTRB(this.widget.sliderHeight * paddingFactor,
2, this.widget.sliderHeight * paddingFactor, 2),
child: Row(
children: <Widget>[
Text(
'${this.widget.min}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: this.widget.sliderHeight * .3,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
SizedBox(
width: this.widget.sliderHeight * .1,
),
Expanded(
child: Center(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white.withOpacity(1),
inactiveTrackColor: Colors.white.withOpacity(.5),
trackHeight: 4.0,
thumbShape: CustomSliderThumbCircle(
thumbRadius: this.widget.sliderHeight * .4,
darkColor: this.widget.color1,
min: this.widget.min,
max: this.widget.max,
),
overlayColor: Colors.white.withOpacity(.4),
),
child: Slider(
value: currentValue,
onChanged: (value) {
setState(() {
currentValue = value;
});
}),
),
),
),
SizedBox(
width: this.widget.sliderHeight * .1,
),
Text(
'${this.widget.max}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: this.widget.sliderHeight * .3,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
],
),
),
);
}
}
Edit: I'm printing it to the console within this widget. It's at the bottom in the onPressed parameter:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Mood', style: TextStyle(fontFamily: 'WorkSans')),
backgroundColor: PrimaryColor,
),
backgroundColor: Color(0xFFF4F1BB),
body: Column(
children: <Widget>[
SizedBox(height: 20),
//Mood title
Align(
alignment: Alignment.center,
child: Text('How are you feeling?',
style: TextStyle(
color: Colors.grey[800],
fontSize: 20,
fontFamily: 'WorkSans'))),
SizedBox(height: 20),
//Mood slider
Align(
alignment: Alignment.center,
child:moodSlider),
SizedBox(height: 20),
//Sleep title
Align(
alignment: Alignment.center,
child: Text('How long did you sleep?',
style: TextStyle(
color: Colors.grey[800],
fontSize: 20,
fontFamily: 'WorkSans'))),
SizedBox(height: 20),
//Sleep slider
Align(
alignment: Alignment.center,
child: //SliderWidget(
// sliderHeight: 48,
// min: 0,
// max: 12,
// color1: 0xFF766E87,
// color2: 0xFF766E87)
sleepSlider),
SizedBox(height: 20),
//Save button
Align(
alignment: Alignment.bottomCenter,
child: RaisedButton(
child: Text('Save'),
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
color: PrimaryColor,
onPressed: () {
print(moodSlider.number);
}, //_save(moodValue, sleepValue),
)),
],
),
);
}
I see one getter named number defined in your code..
int get number => currentValue.round();
..which is defined in _SliderWidgetState class. Not in SliderWidget class. These 2 are 2 different classes. So when you write moodSlider.number, you are not calling the above getter. Because moodSlider is an instance of SliderWidget class and the getter is defined in the other class. Instead, by writing moodSlider.number, what you are doing is - you are accessing the member variable number of the SliderWidget class..
class SliderWidget extends StatefulWidget {
final double sliderHeight;
final int min;
final int max;
final fullWidth;
final int color1;
final int color2;
final int number; //<-- this one here
SliderWidget(
{this.sliderHeight,
this.max,
this.min,
this.fullWidth = false,
this.color1,
this.color2,
this.number});
#override
_SliderWidgetState createState() => _SliderWidgetState();
}
..and this member variable number is never assigned a value. That is why it always prints null.
Edit:
For this to work, we can add an onChanged callback to the SliderWidget, just like the Slider widget has. We can make this callback to accept a int value parameter, so we can round it from inside _SliderWidgetState and pass to the callback. I made a demo on dartpad. You can see it here - https://dartpad.dev/dc8fd327ff5b94edff6c6f6fe3fea17c
As #JigarPatel said, you're calling not calling getter of your State class but you are calling it from the SliderWidget class whose number value is null
You can try updating the SliderWidget to a Stateless Widget See example below:
class SliderWidget extends StatelessWidget {
final double sliderHeight;
final int min;
final int max;
final fullWidth;
final int color1;
final int color2;
final double currentValue;
final ValueChanged<double> onChanged;
SliderWidget(
{this.sliderHeight,
this.max,
this.min,
this.fullWidth = false,
this.color1,
this.color2,
this.currentValue, this.onChanged});
#override
Widget build(BuildContext context) {
double paddingFactor = .2;
if ( fullWidth) paddingFactor = .3;
return Container(
width: fullWidth
? double.infinity
: ( sliderHeight) * 5.5,
height: ( sliderHeight),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(
Radius.circular(( sliderHeight * .3)),
),
gradient: new LinearGradient(
colors: [
Color( color1),
Color( color2),
],
begin: const FractionalOffset(0.0, 0.0),
end: const FractionalOffset(1.0, 1.00),
stops: [0.0, 1.0],
tileMode: TileMode.clamp),
),
child: Padding(
padding: EdgeInsets.fromLTRB( sliderHeight * paddingFactor,
2, sliderHeight * paddingFactor, 2),
child: Row(
children: <Widget>[
Text(
'$min',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: sliderHeight * .3,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
SizedBox(
width: sliderHeight * .1,
),
Expanded(
child: Center(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: Colors.white.withOpacity(1),
inactiveTrackColor: Colors.white.withOpacity(.5),
trackHeight: 4.0,
thumbShape: CustomSliderThumbCircle(
thumbRadius: sliderHeight * .4,
darkColor: color1,
min: min,
max: max,
),
overlayColor: Colors.white.withOpacity(.4),
),
child: Slider(
value: currentValue,
onChanged: onChanged),
),
),
),
SizedBox(
width: sliderHeight * .1,
),
Text(
'$max',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: sliderHeight * .3,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
],
),
),
);
}
}
In onChanged callback you will get the value of the slider.
Current output:
Expected output:
Code:
Align(
alignment: Alignment.topRight,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
margin: EdgeInsets.only(right: 12, top: 8),
decoration: BoxDecoration(
color: Color(0xFF486993),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Text("This is my message"),
),
)
Here is what exactly needed to create neat speech bubble in flutter.
first create a custom painter class that extends Painter class
class CustomChatBubble extends CustomPainter {
CustomChatBubble({this.color, #required this.isOwn});
final Color color;
final bool isOwn;
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color ?? Colors.blue;
Path paintBubbleTail() {
Path path;
if (!isOwn) {
path = Path()
..moveTo(5, size.height - 5)
..quadraticBezierTo(-5, size.height, -16, size.height - 4)
..quadraticBezierTo(-5, size.height - 5, 0, size.height - 17);
}
if (isOwn) {
path = Path()
..moveTo(size.width - 6, size.height - 4)
..quadraticBezierTo(
size.width + 5, size.height, size.width + 16, size.height - 4)
..quadraticBezierTo(
size.width + 5, size.height - 5, size.width, size.height - 17);
}
return path;
}
final RRect bubbleBody = RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(16));
final Path bubbleTail = paintBubbleTail();
canvas.drawRRect(bubbleBody, paint);
canvas.drawPath(bubbleTail, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}
then use it in your chat screen. please note that this screen for only demo purpose so you can modify it according to your needs.
class ChatSampleWidget extends StatefulWidget {
#override
_ChatSampleWidgetState createState() => _ChatSampleWidgetState();
}
class _ChatSampleWidgetState extends State<ChatSampleWidget> {
TextEditingController _editingController = TextEditingController();
FocusNode _focusNode = FocusNode();
final TextStyle textStyle = TextStyle(color: Colors.white);
#override
void initState() {
super.initState();
}
#override
dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chatter'),
),
body: SafeArea(
child: Stack(
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CustomPaint(
painter: CustomChatBubble(isOwn: false),
child: Container(
padding: EdgeInsets.all(8),
child: Text(
'Message from someone else \n Says sometihngs',
style: textStyle,
))),
],
),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CustomPaint(
painter:
CustomChatBubble(color: Colors.grey, isOwn: false),
child: Container(
padding: EdgeInsets.all(10),
child: FlutterLogo())),
],
),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CustomPaint(
painter:
CustomChatBubble(color: Colors.green, isOwn: true),
child: Container(
padding: EdgeInsets.all(8),
child: Text(
'Message from me',
style: textStyle,
))),
],
)
],
),
),
Positioned(
bottom: 0,
child: Container(
padding: EdgeInsets.all(8),
width: MediaQuery.of(context).size.width,
color: Colors.grey.withOpacity(0.1),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Flexible(
child: TextField(
controller: _editingController,
focusNode: _focusNode,
decoration:
InputDecoration(hintText: 'Say something...'),
),
),
IconButton(
icon: Icon(
Icons.send,
size: 30,
),
onPressed: () {
print(_editingController.text);
})
],
),
))
],
),
),
);
}
}
this is how it looks;
Here you are:
import 'package:flutter/material.dart';
void main() => runApp(
MyApp()
);
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Padding(
padding: EdgeInsets.all(7),
child: Align(
alignment: Alignment.centerRight,
child: Stack(
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
decoration: BoxDecoration(
color: Color(0xFF486993),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RichText(
text: TextSpan(
children: <TextSpan>[
TextSpan(
text: 'This is telegram message ',
style: TextStyle(
color: Colors.white,
fontSize: 14.0
),
),
TextSpan(
text: '3:16 PM',
style: TextStyle(
color: Colors.grey,
fontSize: 12.0,
fontStyle: FontStyle.italic
),
),
],
),
),
Icon(Icons.check, color: Color(0xFF7ABAF4), size: 16,)
]
),
),
Positioned(
bottom: 0,
right: 0,
child: CustomPaint(
painter: ChatBubbleTriangle(),
)
)
]
)
),
),
);
}
}
class ChatBubbleTriangle extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
var paint = Paint()..color = Color(0xFF486993);
var path = Path();
path.lineTo(-15, 0);
path.lineTo(0, -15);
path.lineTo(0, 0);
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
You will have to change paint method, because now it's just a triangle.
Otherwise, it looks as you requested.
Trying to recreate Telegram layout myself I came up with this to make the shape;
enum _TailDirection { right, left }
class ChatBubblePainter extends CustomPainter {
ChatBubblePainter({this.color, this.isSentByMe});
final Color color;
final bool isSentByMe;
#override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
Path paintBubbleTail(_TailDirection direction) {
double startingPoint;
double point;
double endPoint;
double curvePoint;
if (direction == _TailDirection.right) {
startingPoint = size.width - 5;
point = size.width + 10;
endPoint = size.width + 3;
curvePoint = size.width;
}
if (direction == _TailDirection.left) {
startingPoint = 5;
point = -10;
endPoint = -3;
curvePoint = 0;
}
return Path()
..moveTo(startingPoint, size.height)
..lineTo(point, size.height)
..quadraticBezierTo(endPoint, size.height, curvePoint, size.height - 10);
}
final RRect bubbleBody = RRect.fromRectAndRadius(
Rect.fromLTWH(0, 0, size.width, size.height), Radius.circular(5.0));
final Path bubbleTail = isSentByMe
? paintBubbleTail(_TailDirection.right)
: paintBubbleTail(_TailDirection.left);
canvas.drawRRect(bubbleBody, paint);
canvas.drawPath(bubbleTail, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}