Possible to have different Button styles in Flutter? - flutter

I have a few OutlineButtons, and I'd like to be able to have multiple styles (dark, light, small, etc).
I can create a ButtonTheme, but how can I specify the Theme or Style for the buttons as they don't accept either parameter?
ie: I'd like the following two buttons to have different background color:
children: {
OutlineButton(color: Colors.black, child: Text("Hello")),
OutlineButton(child:Text("GoodBye"))
}
(The color field is getting ignored as it's using the App Theme's button's background)
Edit: sample code:
Widget button(BuildContext context, String icon, String title, [Color backgroundColor = Colors.white, Color textColor = Colors.black] ) {
return Padding(
padding: const EdgeInsets.only(bottom: 15),
child: Row(
children: <Widget>[
new Expanded(
child: new OutlineButton(
key: null,
shape: RoundedRectangleBorder(),
borderSide: new BorderSide(color: const Color(0xff666666)),
highlightedBorderColor: Color(0xFF303030),
color: backgroundColor,
onPressed: buttonPressed,
child: Row(
children: <Widget>[
icon == null ? Container(width: 0, height: 0) :
new IconButton(
icon: Image.asset(icon),
iconSize: 32.0,
onPressed: () {
},
),
new Expanded(
child: new Text(
title,
textAlign: TextAlign.center,
style: Theme.of(context).primaryTextTheme.button.copyWith(color: textColor)
),
),
],
))),
],
),
);
}
void buttonPressed() {}
and for example, I'm calling it this way:
children: {button(null, "Hello", Colors.black, Colors.white), button(null, "Goodbye", Colors.white, Colors.black)}

You can use add a buttonTheme to your ThemeData and then use the colorSchema property to define colors (you've a set of 30 customizable colors) within your widgets like so:
RaisedButton(color: Theme.of(context).buttonTheme.colorScheme.primary)

Related

Flutter TextButton is there but not displaying

I've been updating a Flutter app and replaced a FlatButton with the new TextButton. But now the button doesn't display on the Card. I can click it and it works, and if I long press you can see the button and it's caption.
The card widget code is below.
Card otherSwapCard(
List<FSRows?> data, int index, context, Function circularprogress) {
String? shiftDate = formatJsonDate(data[index]!.shiftDate!, 'dd/MM/yyyy');
//calculate time value string
String shiftTimes =
'${formatJsonTime24To12(data[index]!.startTime!)} - ${formatJsonTime24To12(data[index]!.finishTime!)}';
return Card(
color: Colors.white,
elevation: 3,
margin: EdgeInsets.fromLTRB(16, 4, 16, 12),
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
width: 2.0,
color: kMainColor40,
),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
flex: 72,
child: Column(
children: <Widget>[
DataKeyRow(dkLabel: 'Job:', dkValue: data[index]!.jobName!),
SizedBox(height: 2),
DataKeyRow(dkLabel: 'Date:', dkValue: shiftDate!),
SizedBox(height: 2),
DataKeyRow(
dkLabel: 'Time:', dkValue: shiftTimes.toLowerCase()),
],
),
),
Expanded(
flex: 28,
child: Center(
child: TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
child: Text('Fill', style: TextStyle(color: Colors.white)),
onPressed: () { },
),
),
),
],
),
),
),
);
}
Card, TextButton, and Text three are in white color. So trying changing the color of the Button or Text.
To change the TextButton color
TextButton(
onPressed: () {},
style: TextButton.styleFrom(
primary: Colors.white,
backgroundColor: Colors.black, // Background Color
),
child: const Text(
'Text Button ',
style: TextStyle(fontSize: 24),
),
)
The backgroundColor will change the TextButton background color to black and the primary will change the font color to white, you can customize it accordingly.
Both your Card color and TextButton Text color are White, you just need to change one of them.
I copied your code and change the color and everything went fine.
Card(
color: Colors.white,
TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
child: Text('Fill', style: TextStyle(color: Colors.**white**)),
onPressed: () { },
),
Card and TextButton both are in white color , so try changing your code.
Change
child: Text('Fill', style: TextStyle(color: Colors.white)),
to
child: Text('Fill', style: TextStyle(color: Colors.black)),

Dynamically adjust text size in buttons inside Row

I am using localization to support multiple languages in my app. This results in having text in buttons with different length. So I need to have it being responsive.
I have two buttons in a Row(). I want to adjust the textsize inside these buttons so they never produce any overflow. Currently it looks like this in some languages:
I tried using auto_size_text with no success.
This is my code for the dialog:
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: kIsWeb ? 40.w : 100.w,
color: Theme.of(context).dialogBackgroundColor,
padding: EdgeInsets.all(15.sp),
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
side: BorderSide(width: 2, color: Theme.of(context).primaryColor),
primary: Colors.black54),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(AppLocalizations.of(context)!.joinGameDialogCancelButton,
style: TextStyle(fontSize: kIsWeb ? 4.sp : 12.sp)),
),
ElevatedButton(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
backgroundColor: Theme.of(context).primaryColor,
primary: Colors.white),
onPressed: () async {
if (formKey.currentState!.validate()) {
Navigator.of(context).pop();
widget.onFinished(nameController.text.trim());
}
},
child: AutoSizeText(
AppLocalizations.of(context)!.joinGameDialogJoinButton,
style: TextStyle(fontSize: kIsWeb ? 4.sp : 14.sp),
overflow: TextOverflow.clip,
stepGranularity: 1,
maxLines: 1,
)
),
],
),
Padding(padding: EdgeInsets.only(top: 15.sp)),
Text("some eula text"),
]))
],
),
)));
You can use FittedBox Widget
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"Your Text Here",
maxLines: 1,
),
),
You can use TextPainter combined without layoutBuilder to determine if the text has overflowed or not, then dynamically resize the text to fit.
Wrapping in a layoutBuilder will determine how much space is available. The textPainter will simulate the rendering of the text and tell you if it has overflowed.
You can save your text size in a variable within the widget state, then decrement the textSize and call setState until the text fits.
See this similar question: How to check if Flutter Text widget was overflowed
Ok, here's what I used: I do not have a mobile device to test it, so I used Windows (but I guess this should not be a problem). In this way, the text gets cut off instead of getting an overflow when it can't get smaller:
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
//width: 100,
color: Theme.of(context).dialogBackgroundColor,
padding: const EdgeInsets.all(15),
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
side: BorderSide(width: 2, color: Theme.of(context).primaryColor),
primary: Colors.black54),
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("weerwerewrweee", style: TextStyle(fontSize: 12)),
),
Expanded(
child: ElevatedButton(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
backgroundColor: Theme.of(context).primaryColor,
primary: Colors.white),
onPressed: () async {
// if (formKey.currentState!.validate()) {
// Navigator.of(context).pop();
// widget.onFinished(nameController.text.trim());
// }
},
child: const AutoSizeText(
"AppLocalizations.of(context)!.joinGameDialogJoinButton,",
style: TextStyle(fontSize: 14),
overflow: TextOverflow.clip,
stepGranularity: 1,
minFontSize: 1,
maxLines: 1,
)),
),
],
),
const Padding(padding: EdgeInsets.only(top: 15)),
const Text("some eula text"),
]))
],
),
)));
The difference with the original code should be only these:
I removed the container width because on Windows it was really too small to test
I wrapped the ElevatedButton in an Expanded following some auto_size_text suggestion
I changed the minFontSize to 1 (obviously too low, but useful for testing)
I put some random texts in the buttons, leaving the second one very long
I removed the onPressed argument just for testing
This is what I got:
big window
small window
The minimum font size must be adjusted, but I think that there's no way to have a readable text AND having maxLines: 1. You probably must choose one of them, or settle for a very small text.
EDIT:
here's how it looks with maxLines: 2:
EDIT 2:
The trick using an empty Expanded to keep the button separated and avoiding a full-width second button:
[...]
const Expanded(child: Text('')),
Expanded(
child: ElevatedButton(
[...]
Result, with short text in the second button:
Please look into the answer i have added spacer() for in between space
Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
//width: 100,
color: Theme.of(context).dialogBackgroundColor,
padding: const EdgeInsets.all(15),
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
side: BorderSide(width: 2, color: Theme.of(context).primaryColor),
primary: Colors.black54),
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("weerwerewrweee", style: TextStyle(fontSize: 12)),
),
Spacer(),
Expanded(
flex: 3,
child: ElevatedButton(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
backgroundColor: Theme.of(context).primaryColor,
primary: Colors.white),
onPressed: () async {
// if (formKey.currentState!.validate()) {
// Navigator.of(context).pop();
// widget.onFinished(nameController.text.trim());
// }
},
child: const Text(
"TestTestTestTestTestTestTestTestTestTestTestTestTestTestTestTest",
style: TextStyle(fontSize: 14),
overflow: TextOverflow.clip,
)),
),
],
),
const Padding(padding: EdgeInsets.only(top: 15)),
const Text("some eula text"),
]))
],
),
)))
SizedBox is used for this condition only.
You can use sizedbox to size any of the Widget.
In your case,
Try with this code -
class CustomButton extends StatelessWidget {
CustomButton();
#override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
child: InkWell(
onTap: () {
Navigator.of(context).pop();
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
color: Theme.of(context).dialogBackgroundColor,
padding: EdgeInsets.all(5),
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: 80,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
side: BorderSide(
width: 2,
color: Theme.of(context).primaryColor),
primary: Colors.black54),
onPressed: () {
Navigator.of(context).pop();
},
child: Text("Raushan is flutter developer",
style: TextStyle(fontSize: 12)),
),
),
SizedBox(
width: 80,
child: ElevatedButton(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20.0)),
backgroundColor:
Theme.of(context).primaryColor,
primary: Colors.white),
onPressed: () async {},
child: Text("Raushan is flutter developer",
style: TextStyle(fontSize: 12)),
),
)
],
),
Padding(padding: EdgeInsets.only(top: 15)),
Text("some eula text"),
]))
],
),
)));
}
}
I think this will fix your issue.
No need of using any dependency
Use custom class AdaptableText in your project.
adaptable_text.dart
import 'package:flutter/cupertino.dart';
class AdaptableText extends StatelessWidget {
final String text;
final TextStyle? style;
final TextAlign textAlign;
final TextDirection textDirection;
final double minimumFontScale;
final TextOverflow textOverflow;
const AdaptableText(this.text,
{this.style,
this.textAlign = TextAlign.left,
this.textDirection = TextDirection.ltr,
this.minimumFontScale = 0.5,
this.textOverflow = TextOverflow.ellipsis,
Key? key})
: super(key: key);
#override
Widget build(BuildContext context) {
TextPainter _painter = TextPainter(
text: TextSpan(text: this.text, style: this.style),
textAlign: this.textAlign,
textScaleFactor: 1,
maxLines: 100,
textDirection: this.textDirection);
return LayoutBuilder(
builder: (context, constraints) {
_painter.layout(maxWidth: constraints.maxWidth);
double textScaleFactor = 1;
if (_painter.height > constraints.maxHeight) { //
print('${_painter.size}');
_painter.textScaleFactor = minimumFontScale;
_painter.layout(maxWidth: constraints.maxWidth);
print('${_painter.size}');
if (_painter.height > constraints.maxHeight) { //
//even minimum does not fit render it with minimum size
print("Using minimum set font");
textScaleFactor = minimumFontScale;
} else if (minimumFontScale < 1) {
//binary search for valid Scale factor
int h = 100;
int l = (minimumFontScale * 100).toInt();
while (h > l) {
int mid = (l + (h - l) / 2).toInt();
double newScale = mid.toDouble()/100.0;
_painter.textScaleFactor = newScale;
_painter.layout(maxWidth: constraints.maxWidth);
if (_painter.height > constraints.maxHeight) { //
h = mid - 1;
} else {
l = mid + 1;
}
if (h <= l) {
print('${_painter.size}');
textScaleFactor = newScale - 0.01;
_painter.textScaleFactor = newScale;
_painter.layout(maxWidth: constraints.maxWidth);
break;
}
}
}
}
return Text(
this.text,
style: this.style,
textAlign: this.textAlign,
textDirection: this.textDirection,
textScaleFactor: textScaleFactor,
maxLines: 100,
overflow: textOverflow,
);
},
);
}
}
Now use this class
Container(
width: 250,
height: 20,
color: Colors.green,
child: AdaptableText(mediumSizeText, style: const TextStyle()),
),
Here i am also showing the difference between normal text, text inside size box and adaptive text
return Scaffold(
appBar: AppBar(
title: const Text('ExpandableText'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Normal Text'),
Text(mediumSizeText),
const SizedBox(
height: 20,
),
const Text('Container(250*20) Normal Text:'),
Container(
width: 250,
height: 20,
color: Colors.green,
child: Text(
mediumSizeText,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(
height: 20,
),
const Text('Container(250*20) => sizebox => Text:'),
Container(
width: 250,
height: 20,
color: Colors.green,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Text(
mediumSizeText,
maxLines: 100,
),
),
),
const Text('Container(250*20) => AdaptableText => Text:'),
Container(
width: 250,
height: 20,
color: Colors.green,
child: AdaptableText(mediumSizeText, style: const TextStyle()),
),
],
),
);
Here is the result:

Can I add a title decoration to a Material Card in Flutter?

Is there a way to add a title box to a card in the Flutter Material design? So far I've got to:
Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
side: BorderSide(color: Colors.black)
),
child: ...
)
But I can't figure out how to add the title decoration. Is there a way? The picture below is roughly what I'm trying to achieve.
In material design the title of a card wouldn't float over the border, as your picture - although it is possible to make, as we see in textFormField decoration.
Below a code of Material 2 for building a card. You can find other examples and how to theme your card in the link: https://material.io/components/cards/flutter#card
Card(
clipBehavior: Clip.antiAlias,
child: Column(
children: [
ListTile(
leading: Icon(Icons.arrow_drop_down_circle),
title: const Text('Card title 1'),
subtitle: Text(
'Secondary Text',
style: TextStyle(color: Colors.black.withOpacity(0.6)),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Greyhound divisively hello coldly wonderfully marginally far upon excluding.',
style: TextStyle(color: Colors.black.withOpacity(0.6)),
),
),
ButtonBar(
alignment: MainAxisAlignment.start,
children: [
FlatButton(
textColor: const Color(0xFF6200EE),
onPressed: () {
// Perform some action
},
child: const Text('ACTION 1'),
),
FlatButton(
textColor: const Color(0xFF6200EE),
onPressed: () {
// Perform some action
},
child: const Text('ACTION 2'),
),
],
),
Image.asset('assets/card-sample-image.jpg'),
Image.asset('assets/card-sample-image-2.jpg'),
],
),
),

how to change the position of the button in the flutter

I have a problem, it's not difficult, but I can't solve it. I have a task book program. At the top is the word "task", this word is in the middle as needed. But there is also a button (add). The problem is that I can't put it closer to the center, to the word "task"! I'll be grateful if someone can help me move this button to the center to the level of the word "task"
My code :
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 3,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 40.0),
child: Text(("Tasks"),
style: TextStyle(fontSize: 70.0,
color: Colors.black,
fontWeight: FontWeight.bold)),
)
),
Expanded(
child: FlatButton(
textColor: Colors.black,
color: Colors.white12,
child: Text('+',style: TextStyle(fontSize: 70.0,
color: Colors.black,
)),
onPressed: () {
if (textController.text.isNotEmpty) {
WidgetList.add(
new ListItem(textController.text, false));
setState(() {
valText = true;
textController.clear();
});
}
else
{
setState(() {
valText = false;
});
}
},
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),)
],
),
),
Try to not use FlatButton cause' it is deprecated, and as you use an icon to create a button, use IconButton instead :
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, // this is to center both
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 24.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
IconButton(
color: Colors.black,
iconSize: 28,
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
icon: const Icon(Icons.add_outlined),
onPressed: () {},
)
],
),
),
Ok, the contraints in the IconButton, in less words it helps you to be close between the Text and the Icon and delete the padding (but not all), aaaand if you add padding: EdgeInsets.zero, literally it deletes all the padding to zero. Try to add some padding, to fix the "closer"

Flutter positioned widget cuts off content

I'm attempting to create a notification widget with a number overlay.
I've followed the code here to make the widget.
Using this code (not specifying a position), produces the following widget:
Widget iconWidget() {
return Stack(
children: <Widget>[
Center(
child: Container(
child: Icon(
icon,
color: color,
),
)),
Positioned(
child: CircleAvatar(
child: Text('$count',
style: TextStyle(fontSize: 12, color: Colors.white)),
backgroundColor: count == 0 ? Colors.grey : Colors.black,
),
)
],
);
}
However, as soon as I specify a position (right: 0), my widget gets gut off:
Widget iconWidget() {
return Stack(
children: <Widget>[
Center(
child: Container(
child: Icon(
icon,
color: color,
),
)),
Positioned(
right: 0,
child: CircleAvatar(
child: Text('$count',
style: TextStyle(fontSize: 12, color: Colors.white)),
backgroundColor: count == 0 ? Colors.grey : Colors.black,
),
)
],
);
}
My widget is being created as an icon within a tab controller:
Tab(
icon: IconWithCount(
icon: FlutterIcons.heartbeat_faw5s,
color: Colors.red,
count: 5)
.iconWidget(),
),
Full class creating the widget:
class IconWithCount {
final IconData icon;
final int count;
final Color color;
IconWithCount({
this.icon,
this.count,
this.color,
});
Widget iconWidget() {
return Stack(
children: <Widget>[
Center(
child: Container(
child: Icon(
icon,
color: color,
),
)),
Positioned(
right: 0,
child: CircleAvatar(
child: Text('$count',
style: TextStyle(fontSize: 12, color: Colors.white)),
backgroundColor: count == 0 ? Colors.grey : Colors.black,
),
)
],
);
}
}
overflow is Deprecated. Use this:
Stack(
clipBehavior: Clip.none, // <---
children: [],
)
Inside the stack , just add this
overflow: Overflow.visible,