How to change the Flutter TextButton height? - flutter

This is the output:
I try to make an app but when I use the TextButton, I get the space between two Buttons
I need one by one without space
If I use the Expanded Widget, ScrollChildView doesn't work
I try but I can't clear this stuff.
I try to make this type of TextButton.
Anyone know or have any idea about this?
import "package:flutter/material.dart";
import 'package:audioplayers/audio_cache.dart';
class Account extends StatefulWidget {
Account({Key key}) : super(key: key);
#override
_AccountState createState() => _AccountState();
}
class _AccountState extends State<Account> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Stack(
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextButton(
child: Container(
child: Text(
'One',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Colors.red),
),
onPressed: () {
final player = AudioCache();
player.play('note1.wav');
},
),
SizedBox(
height: 1,
),
TextButton(
child: Container(
child: Text(
'Two',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Colors.green),
),
onPressed: () {
final player = AudioCache();
player.play('note2.wav');
},
),
TextButton(
child: Container(
child: Text(
'Three',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Colors.blue),
),
onPressed: () {
final player = AudioCache();
player.play('note3.wav');
},
),
TextButton(
child: Container(
child: Text(
'Four',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Colors.grey),
),
onPressed: () {
final player = AudioCache();
player.play('note4.wav');
},
),
TextButton(
child: Container(
child: Text(
'Five',
style: TextStyle(color: Colors.white, fontSize: 10),
),
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(Colors.purple),
),
onPressed: () {
final player = AudioCache();
player.play('note5.wav');
},
),
],
),
),
],
),
),
),
);
}
}

You just wrap your text button with the SizedBox and set height and width as follows:
SizedBox(
height: 30,
width: 150,
child: TextButton(...),
)

Full available height:
SizedBox(
height: double.infinity, // <-- match_parent
child: TextButton(...)
)
Specific height:
SizedBox(
height: 100, // <-- Your height
child: TextButton(...)
)

In Flutter 2.0, you can set the height of the TextButton directly without depending on other widgets by changing the ButtonStyle.fixedSize:
TextButton(
child: Text('Text Button'),
style: TextButton.styleFrom(fixedSize: Size.fromHeight(150)),
),
If you want to modify all TextButtons, put it in the ThemeData like below:
return MaterialApp(
theme: ThemeData(
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(fixedSize: Size.fromHeight(150)),
),
),
Live Demo

use the following to even add your preferred size as well
N/B: child sized box is the main child widget inside Padding widget
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(30.0),
child: SizedBox(
height: 60,
width: 200,
child: ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RegistrationMenu()));
},
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.red.shade800),
),
icon: Icon(Icons.person_add_alt_1_rounded, size: 18),
label: Text("Register Users"),
),
),
),
],
),

Wrap the TextButton in an "Expanded" Widget.
Expanded(
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.red,
padding: EdgeInsets.zero,
),
child: const Text(''),
onPressed: () {
playSound(1);
},
),
),

Another solution would be wrapping with ConstrainedBox and using minWidth & minHeight
properties.
ConstrainedBox(
constraints:BoxConstraints(
minHeight:80,
minWidth:200
),
child:TextButton(..)
)

You need to do two things
Determine text button height using
Sizedbox
Remove padding around text button using
TextStyle.StyleFrom()
SizedBox(
height: 24.0,
child: TextButton(
onPressed: () {},
child: Text('See more'),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
),
),

TextButton(
 onPressed: _submitOrder,
 child: Padding(
     padding: EdgeInsets.only(left: 20, right: 20),
     child: Text("Raise")),
 style: ButtonStyle(
   shape: MaterialStateProperty.all(
   const RoundedRectangleBorder(
   borderRadius: BorderRadius.all(Radius.circular(50)),
   side: BorderSide(color: Colors.green)))),
)

TextButton(
style: ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap
),
child: Text(''),
);

Related

Flutter how to add a dialog screen over main screen like this

Hi all,
I would like to add a screen that slowly appears form the bottom or the screen and partially covers the main screen below. So you can still see the top part of the main screen. Does anyone know how to do this?
Thank you very much
for this you can use showModalBottomSheet method the simple example is
import 'package:flutter/material.dart';
void main() => runApp(const BottomSheetApp());
class BottomSheetApp extends StatelessWidget {
const BottomSheetApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Bottom Sheet Sample')),
body: const BottomSheetExample(),
),
);
}
}
class BottomSheetExample extends StatelessWidget {
const BottomSheetExample({super.key});
#override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
child: const Text('showModalBottomSheet'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
color: Colors.amber,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text('Modal BottomSheet'),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
),
],
),
),
);
},
);
},
),
);
}
}
you can read more about this method here
You can use showModalBottomSheet() same as below...
showModalBottomSheet<void>(
// context and builder are
// required properties in this widget
context: context,
builder: (BuildContext context) {
// we set up a container inside which
// we create center column and display text
// Returning SizedBox instead of a Container
return SizedBox(
height: MediaQuery.of(context).size.height * 0.6,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('HERE You'll add all your content'),
],
),
),
);
},
);
You can call above method in
initState() of screen or buttons onPressed or onTap.
As per your shared Image I have try something like that Using ModalBottomSheet
Your Button Widget
ElevatedButton(
child: const Text('Show Modal BottomSheet'),
onPressed: () {
showModalBottomSheet<void>(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
),
context: context,
builder: (BuildContext context) {
return modelSheet(context);
},
);
},
)
bottomSheet Widget:
modelSheet(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(25.0)),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(
Icons.hourglass_empty_outlined,
color: Colors.red,
size: 40,
),
const SizedBox(
height: 10,
),
const Text(
'Beta version',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.check,
color: Colors.red,
),
Text('better price')
],
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.check,
color: Colors.red,
),
Text('early access')
],
),
const SizedBox(
height: 20,
),
RichText(
text: const TextSpan(
text:
'Please mind that this is a beta version of the app. As a founding member you can get',
style: TextStyle(fontSize: 20, color: Colors.black),
children: <TextSpan>[
TextSpan(
text: '50% off',
style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: ' the price & early access. !'),
],
),
),
const SizedBox(
height: 20,
),
const Text(
'You can look forward to more teachers and practices very soon.'),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
fixedSize: const Size(double.maxFinite, 50)),
child: const Text('Got it'),
),
],
),
),
);
}
Result Screen->

Flutter Inkwell Ontap not working inside a Stack

I am building a flutter ecommerce app and I am having an issue where ontap isn't working inside my inkwell widget. I want the ontap to work so that I can show the product description. I have placed the inkwell widget as a child inside a Positioned widget, which happens to be one of the children of a Stack. How can I solve this?
Here's my code:
return Card(
shadowColor: Colors.grey,
surfaceTintColor: Colors.amber,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Stack(
children: [
Positioned(
right: 0,
child: InkWell(
onTap: () {
print('tapped');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProductDetails(
id: bottleCategory
.bottleList[index].id,
bottleName: bottleCategory
.bottleList[index]
.bottleName,
image: bottleCategory
.bottleList[index]
.image,
price: bottleCategory
.bottleList[index]
.price)));
},
child: IconButton(
icon: favoriteProvider.isExist(
bottleCategory.bottleList[index])
? Icon(
Icons.favorite,
color: Colors.redAccent,
)
: Icon(
Icons.favorite_border,
),
onPressed: (() {
favoriteProvider.toggleFavorites(
bottleCategory.bottleList[index]);
if (favoriteProvider.isExist(
bottleCategory.bottleList[index])) {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Added to Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
),
);
} else {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Removed from Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.red,
duration: Duration(seconds: 1),
),
);
}
}),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Image.asset(
bottleCategory.bottleList[index].image,
height: 200.0,
),
),
Center(
child: Text(
bottleCategory
.bottleList[index].bottleName,
style: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold))),
Center(
child: Text(
'R${bottleCategory.bottleList[index].price}'),
)
],
),
Positioned(
bottom: 0,
right: 10,
child: IconButton(
icon: const Icon(Icons.add_circle),
iconSize: 40.0,
onPressed: () {
cart.addToCart(
bottleCategory.bottleList[index].id,
bottleCategory
.bottleList[index].bottleName,
bottleCategory
.bottleList[index].price,
bottleCategory
.bottleList[index].image);
},
))
],
),
);
Why do you use IconButton in the child of InkWell?
use one of them and Put all of your onPress functions into that widget.

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:

How to fix a non-null String must be provided to a Text widget

this is part of my code, I keep getting a non-null String must be provided to a Text widget.
Still new to flutter, so I'm not sure on how to fix this. I tried putting the ??"" on child: Text(myQuiz[0][i.toString()] but then it gave me an error on The method '[]' was called on null.
Receiver: null
Tried calling:
Widget optionButton(String k) {
return Padding(
padding: EdgeInsets.only(
top: 10
),
child: MaterialButton(
onPressed: () => checkAns(k),
child: Text(
myQuiz[1][i.toString()][k],
style: TextStyle(
color: Colors.black,
fontFamily: "Open Sans",
fontSize: 16.0,
),
maxLines: 1,
),
color: buttonColor[k],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(18.0),
side: BorderSide(color: Color(0xffb0dab9))),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () {
return showDialog(
context: context,
builder: (context) => AlertDialog(
content: Text("You must finish this quiz :)"),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Ok',
),
)
],
));
},
child: Scaffold(
appBar: AppBar(title: Text('Quiz'),backgroundColor: Color(0xffb0dab9)),
backgroundColor: Colors.yellow[100],
body: Container(
padding: EdgeInsets.all(50),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
alignment: Alignment.center,
child: Text(myQuiz[0][i.toString()],
style: TextStyle(fontSize: 20.0, color: Colors.black),
),
),
AbsorbPointer(
absorbing: disableAnswer,
child: Container(
padding: EdgeInsets.only(top: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
optionButton('a'),
optionButton('b'),
optionButton('c'),
optionButton('d'),
],
),
),
),
Container(
padding: EdgeInsets.only(top: 30),
alignment: Alignment.topCenter,
child: Center(
child: Text(
showTimer ,
style: TextStyle(
fontSize: 20.0,
),
),
),
),
],
),
),
),
)
);
}
}
It is because a null value is being provide to your Text widget
From your code.. one or more of the following is null and not a string
myQuiz[1][i.toString()][k]
showTimer
Try
...
#override
Widget build(BuildContext context) {
print(myQuiz[1][i.toString()][k]);
print(showTimer);
to find out which is the null value
Try this.
Text(
myQuiz[1]?[i?.toString()]?[k] ?? "Default Value",
style: TextStyle(
color: Colors.black,
fontFamily: "Open Sans",
fontSize: 16.0,
),
maxLines: 1,
),
Thanks for helping, I have found the answer, it was in the getRandom, as the json does not have a 0, so when the quiz runs and hit 0, it causes the error

Rounded AppBar in Flutter with Back button

I created a custom rounded AppBar using a code found here, but with just a title in the center.
I wanted to add a backbutton in the top left corner inside AppBar and I tried nesting a button and the text in a Row, but the result is that neither the button or the text are shown. Any help?
Here the code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class RoundedAppBar extends StatelessWidget implements PreferredSizeWidget {
String title;
RoundedAppBar(this.title);
#override
Widget build(BuildContext context) {
return PreferredSize(
child: LayoutBuilder(builder: (context, constraints) {
final width =
constraints.maxWidth * 16; //per modificare "rotondità" app Bar
return OverflowBox(
maxHeight: double.infinity,
maxWidth: double.infinity,
child: SizedBox(
height: width,
width: width,
child: Padding(
padding: EdgeInsets.only(
bottom: width / 2 - preferredSize.height / 2),
child: Container(
alignment: Alignment.bottomCenter,
padding: EdgeInsets.only(bottom: 10),
decoration: BoxDecoration(
color: const Color(0xff000350),
shape: BoxShape.circle,
),
child: Row(
children: [
Align(
alignment: Alignment.centerLeft,
child: IconButton(
color: Colors.black,
icon: Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context),
),
),
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Conformity',
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.normal),
),
],
)),
),
),
);
}),
preferredSize: preferredSize);
}
#override
Size get preferredSize => Size.fromHeight(80);
EDIT:
Tried using ListTile as suggested, something happened but didn't work properly.
Here the result.
child: ListTile(
title: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Conformity',
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.normal),
),
leading: IconButton(
color: Colors.white,
icon: Icon(Icons.chevron_left),
onPressed: () => Navigator.pop(context),
),
),
EDIT:
I inserted your code as shown. With trial and error, using 35 as height I was able to see the title, but still no button.
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_buildBack(true, context),
Container(
height: 35,
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Conformity',
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.normal),
),
),
_buildBack(false, context),
],
and
Widget _buildBack(bool isPlaceHolder, BuildContext context) {
return Visibility(
child: InkWell(
child: Icon(
Icons.close,
size: 35,
),
onTap: () => Navigator.of(context, rootNavigator: true).pop('dialog'),
),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: !isPlaceHolder,
);
}
and here the result
You can use a ListTile and use a IconButton as leading.
ListTile(
leading: IconButton(
icon: Icon(Icons.back),
title: '',
onPressed => Navigator.pop(context),
),
),
Another possibility I see:
As the child from the AppBar
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_buildBack(true, context),
Container(
height: height,
child: Text(
'$_title',
style: Theme.of(context).textTheme.headline2,
),
),
_buildBack(false, context),
],
),
In another place outside the builder.
Widget _buildBack(bool isPlaceHolder, Buildcontext context) {
return Visibility(
child: InkWell(
child: Icon(
Icons.close,
size: widget.height,
),
onTap: () => Navigator.of(context, rootNavigator: true).pop('dialog'),
),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: !isPlaceHolder,
);
}}
Here there is again a row as you have tried it yourself, but this one is set up a little differently and an iconButton is built before and after the text, but so that the text remains in the center, the second one is made invisible,