I'm trying to make all Text(length varies) in a fixed width box to fit and I want them to look at the same size as the longest word's size when FittedBox applied. So What I'm doing to achieve that is filling the rest of the word with empty space to match the length to the longest word. But that doesn't quite work as you see in the following image:
Here is the code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
'/': (context) => HomePage(),
},
);
}
}
class HomePage extends StatelessWidget {
final String padding = " " * 5;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Container(
color: Colors.red,
height: 100.0,
width: 100.0,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"${padding}Demo$padding",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black),
),
),
),
Icon(Icons.mic, size: 24.0),
],
),
),
),
);
}
}
Do you have any idea what is the issue here?
It's really strange, but it seems to work correctly if you remove alignment or set it to start. (What cause this bug - I still don't know)
child: Text('${padding}Demo$padding',
style: TextStyle(color: Colors.black, decoration: TextDecoration.underline),)
I've added decoration to see these spaces
Related
I'm building List of questions for my Quizzler app and want to access the question one by one but can't able to add the list of my questions in Text function of my app... So how should I add my list of strings to Text to display them on screen one by one?
enter image description here
class QuizPage extends StatefulWidget {
#override
_QuizPageState createState() => _QuizPageState();
}
class _QuizPageState extends State<QuizPage> {
List<Widget> scorekeeper = [];
List<String> questions = [
'You can lead a cow down stairs but not up stairs.',
'Approximately one quarter of human bones are in the feet.',
'A slug\'s blood is green.'
];
int questionNum = 0;
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const Expanded(
flex: 3,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
questions[questionNum],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
),
You can use ListView.builder like this:
ListView.builder(
itemCount: questions.length,
itemBuilder: (_, i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
questions[i],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
color: Colors.white,
),
)
)
);
}
);
And remove key word const.
I created a small application with two dice. It works perfectly like I wanted to do it. For the first time, I have a problem with changing the background color of this page. As you can see in my Code below, I chose the backgroundcolor "teal". I have no idea, why this Background doesn't change to "teal". On all the other pages of my App, the Backgroundcolor is "teal"
Can someone help me with this problem.
Here's the complete code of this page:
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
return runApp(
MaterialApp(
home: Scaffold(
backgroundColor: Colors.teal,
body: DicePage(),
),
),
);
}
class DicePage extends StatefulWidget {
#override
_DicePageState createState() => _DicePageState();
}
class _DicePageState extends State<DicePage> {
int leftDiceNumber = 1;
int rightDiceNumber = 1;
void changeDiceFace() {
setState(() {
leftDiceNumber = Random().nextInt(6) + 1;
rightDiceNumber = Random().nextInt(6) + 1;
});
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: FlatButton(
onPressed: () {
setState(() {
leftDiceNumber = Random().nextInt(6) + 1;
});
},
child: Image.asset('images/dice$leftDiceNumber.png'),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(40.0),
child: FlatButton(
onPressed: () {
changeDiceFace();
},
child: Image.asset('images/dice$rightDiceNumber.png'),
),
),
),
],
),
);
}
}
If you are using same background color for all screens would consider changing it via theme:
MaterialApp(
theme:ThemeData(scaffoldBackgroundColor: Colors.teal),
home: Scaffold(...
In your MaterialApp there is one method name ThemeData. You can set theme of your app through this. You can follow below code
void main() {
return runApp(
MaterialApp(
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: Scaffold(
body: DicePage(),
),
),
);
}
I ran your codes except the images part and the background color shows correctly. I think you can check your images but not the codes.
I created an application in android studio to navigate from one screen to another.Here two stateless widgets are created as two screens and both contain a button to navigate pages each other.
However when i run the application a red screen is generated on my android phone I get an error saying
exception 'Column's children must not contain any null values, but a null value was found at index 0'.
I have provided my code below:
FIRST SCREEN
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("First Screen"),
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
center(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/new 7wonders.jpg'),
fit: BoxFit.cover,
),
),
),
Text('New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
);
}
center({BoxDecoration decoration}) {}
}
SECOND SCREEN
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: RaisedButton(
child: Text("Go to First page"),
onPressed:() {
Navigator.pop(context);
},
),
);
}
}
Your center method should return a Widget, it is currently providing null to the Column.
Do this instead:
Widget center() {
// return a decorated box widget which gives you the decoration property
return Image(
image: AssetImage(
'assets/new 7wonders.jpg',),
fit: BoxFit.cover,
);
}
}
Then use in your Column like :
Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// call the center method which returns a Widget
center(),
Text(
'New 7 Wonders',
style: TextStyle(fontSize: 40, fontStyle: FontStyle.italic),
),
RaisedButton(
child: Text("Bang Here"),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondScreen()));
},
color: Colors.red,
textColor: Colors.yellow,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.grey,
)
],
),
),
),
you have to return any widget in center
center({BoxDecoration decoration}) {
return Container();
}
You tryed write Center instead center in line 24?
And in Center must be will return for example Containter()
In 24th line, you returned null value. You can implement the center method like this;
return Container();
Remove center use this
Container(
height: 100, // height and width according to your ui
width:100,
child:Image.asset(('assets/new7wonders.jpg',fit: BoxFit.cover,), // use for local image from asset and please change image name in code as well as in asset folder.there should not be space between image name .
),
I have several modalBottomSheet in my app to show, most of them have simple widget tree except only one that has 10 DropDownBottom widgets in it
Each one of them load about 200 items, each item is a widget consist of two main widgets a text and an image
when I Press
onPressed: () {
showModalBottomSheet(context: context, builder: picksWidget, isScrollControlled: true);
}
It take about 3 seconds to open load the modalBottomSheet and it just appear into the emulator without the sliding up animation, other modalBottomSheets in the app load perfectly fine, here is an example of the code I use.
import 'package:flutter/material.dart';
import 'package:myapp/data/picked.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:myapp/data/picks.dart';
import 'package:myapp/data/Icons.dart';
Widget buildPickerBottomSheet(BuildContext context) {
return Wrap(children: <Widget>[
PickerList(),
]);
}
class PickerList extends StatelessWidget {
const PickerList({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: Row(
children: <Widget>[
AutoSizeText(
'Pick ',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
maxLines: 1,
),
],
),
),
SizedBox(
height: 5,
),
PickerRow(
typeOne: 'vr',
typeTwo: 'er',
type: 'r',
),
PickerRow(
typeOne: 'vq',
typeTwo: 'eq',
type: 'q',
),
PickerRow(
typeOne: 'vw',
typeTwo: 'ew',
type: 'w',
),
PickerRow(
typeOne: 'vz',
typeTwo: 'ez',
type: 'z',
),
PickerRow(
typeOne: 'vy',
typeTwo: 'ey',
type: 'y',
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
child: Text('Add'),
onPressed: () async {
print('added');
},
),
RaisedButton(
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
)
],
),
SizedBox(
height: 50,
)
],
);
}
}
class PickerRow extends StatelessWidget {
final String typeOne;
final String typeTwo;
final String type;
PickerRow({#required this.typeOne, #required this.typeTwo, #required this.type});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
DropDownMenu(
pickType: typeOne,
),
Container(
width: 2,
height: 30,
color: Colors.blue,
),
Container(
height: 30,
width: 30,
child: Image(
image: AssetImage(AppIcons.types[type]),
),
),
Container(
width: 2,
height: 30,
color: Colors.red,
),
DropDownMenu(
pickType: typeTwo,
),
],
),
);
}
}
class DropDownMenu extends StatefulWidget {
//which position will this pick for
final String pickType;
DropDownMenu({#required this.pickType});
#override
_DropDownMenuState createState() => _DropDownMenuState();
}
class _DropDownMenuState extends State<DropDownMenu> {
//get a list of the picks to display in the drop down
static List<DropdownMenuItem> getDropDownItems() {
List<DropdownMenuItem<String>> dropDownItems = [];
for (int i = 0; i < Picks.picksNames.length; i++) {
String pick = Picks.picksNames[i];
var newItem = DropdownMenuItem(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: AssetImage(AppIcons.picks[pick]),
radius: 15,
),
SizedBox(
width: 4,
),
AutoSizeText(
pick,
maxLines: 1,
),
],
),
value: pick,
);
dropDownItems.add(newItem);
}
return dropDownItems;
}
var items = getDropDownItems();
#override
Widget build(BuildContext context) {
String chosenItem = Picked.selection[widget.pickType];
return DropdownButton<String>(
value: chosenItem,
items: items,
onChanged: (value) {
setState(() {
chosenItem = value;
});
Picked.selection[widget.pickType] = value;
},
);
}
}
I am new to development in general so I appreciate if there is any resources on how to measure and improve performance on flutter apps.
Thanks.
It seems like your bottomModalSheet is too heavy. It loads only after the children have completed building.
Resize the images that you use inside the bottomModalSheet.
Usually the debug app is very slow compared to the release app.
Generate the apk of the app: flutter build apk --split-per-abi.
Install the generated apk build/app/outputs/apk/release/app-armeabi-v7a-release.apk and see if the problem still persists.
You should avoid rendering 2000 widgets at once.
First of all look at ListView widget and it's builder(). Try to refactor your code and put all your items inside this ListView. It will increase your performance a lot due to the reason that ListView.builder() allows you not to store all your widgets in memory all the time.
https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html
I have a long text and I need to show the scrollbar by default when the user enters my page.
Currently, the bars not shown until the user click over the text and this, not good behavior because the user could leave the page without notice that there is some unread text.
My code:
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
Image.asset(
"assets/images/logo.png",
height: 200.0,
),
SizedBox(
height: 40,
),
Expanded(
child: Scrollbar(
child: SingleChildScrollView(
child: Text("Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
SizedBox(
height: 50,
),
Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text("Continue"),
onPressed: () {
MaterialPageRoute route = MaterialPageRoute(
builder: (BuildContext context) => MainPage());
Navigator.of(context).push(route);
},
),
),
SizedBox(
width: 20.0,
),
RaisedButton(
child: Text("Close"),
onPressed: () {
exit(0);
},
),
],
)
],
),
),
),
);
}```
As of Flutter version 1.17, on Scrollbar you can set isAlwaysShown to true, but you must set the same controller for your Scrollbar and your SingleChildScrollView (and that applies to any other scrollable Widget as well).
Have in mind that, for the Scrollbar to be visible, there must be enough items to scroll. If there are not, the Scrollbar won't be shown.
Full working example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: MyWidget(),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: <Widget>[
// ...
Expanded(
child: Scrollbar(
controller: _scrollController, // <---- Here, the controller
isAlwaysShown: true, // <---- Required
child: SingleChildScrollView(
controller: _scrollController, // <---- Same as the Scrollbar controller
child: Text(
"Long Text Here ...",
textDirection: TextDirection.rtl,
style: TextStyle(fontSize: 17.2),
),
),
),
),
// ...
],
),
),
),
);
}
}
As of v2.9.0-1.0, thumbVisiblity is the proper field to set.
Note you can set this globally for your app (or a certain subtree) using ScrollbarTheme:
return MaterialApp(
...
theme: ThemeData(
...
scrollbarTheme: ScrollbarThemeData(
thumbVisibility: MaterialStateProperty.all<bool>(true),
)
)
)
It's good to prefer themes for styling like this, so avoid doing more than once.
You'll still need to add a Scrollbar and Controller as described in other answers though.
Use draggable_scrollbar package. It provides a dragable scrollbar with option to make the scrollbar always visible. For example, you can use the following code
DraggableScrollbar.arrows(
alwaysVisibleScrollThumb: true, //use this to make scroll thumb always visible
labelTextBuilder: (double offset) => Text("${offset ~/ 100}"),
controller: myScrollController,
child: ListView.builder(
controller: myScrollController,
itemCount: 1000,
itemExtent: 100.0,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Material(
elevation: 4.0,
borderRadius: BorderRadius.circular(4.0),
color: Colors.purple[index % 9 * 100],
child: Center(
child: Text(index.toString()),
),
),
);
},
),
);
'isAlwaysShown' is deprecated and shouldn't be used. Use thumbVisibility instead.
Example:
Scrollbar(
controller: ScrollController(),
thumbVisibility: true,
child: SingleChildScrollView(
child: Column(
thumbVisibility make true for show always scroll bar for list in scrollbar widget
Scrollbar(thumbVisibility: true,)
You can change the scrollbartheme to set flag isAlwaysShown true
scrollbarTheme: const ScrollbarThemeData(isAlwaysShown: true)