I'm using Provider in Flutter for state management. And I want to display some text in my widget using this Provider. The test is shown but is looks very strange height or padding I don't know. So here is a code.
class JobDetailsScreen extends HookWidget {
const JobDetailsScreen({required this.id, Key? key}) : super(key: key);
final String id;
#override
Widget build(BuildContext context) {
final job = Provider.of<JobsNotifier>(context).currentJob;
var loading = Provider.of<JobsNotifier>(context).isLoadingCurrentJob;
useEffect(() {
if (job == null) {
Future.microtask(() async {
await Provider.of<JobsNotifier>(context, listen: false)
.getCurrentJob(id);
});
}
return () => {};
}, [id]);
return Scaffold(
appBar: const NavBarTop(
title: 'Job Details',
innerAppBar: true,
),
body: loading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(job.title,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 24)),
Text(job.company,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 18)),
ElevatedButton(
onPressed: () async {
try {
await openUrl(job.applyUrl!);
} on String catch (e) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
e,
style: const TextStyle(color: Colors.white),
),
backgroundColor: Colors.red,
));
}
},
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(
const Size(double.infinity, 50))),
child: const Text('Apply'),
),
],
),
),
),
);
}
}
And I see this on my screen
Screen with wrong text behaviour
I'm expecting widget to show text the right way without any paading and some redundant height.
It should be like this:
Right text behaviour screen
The response might contain newlines. Like maybe job.title equals "\nUI/UI Design Lead\n\n".
Try use job.title.trim() so any leading and trailing whitespaces and newlines are removed.
i have tried to run your code, and it seems to work fine without any padding to the text widgets or height, heres's a screenshot:
i believe what you can do is to check through you NavBarTop widget, to see if there could be any property affecting this.
Related
I have a drawer menu in my scaffold, and I want to show some information from Flutter secure storage.
class DrawerMenu extends StatefulWidget {
final Translations translations;
final PageController controller;
const DrawerMenu({
Key? key,
required this.translations,
required this.controller,
}) : super(key: key);
#override
State<DrawerMenu> createState() => _DrawerMenuState();
}
String? name;
String? email;
final FlutterSecureStorage storage = FlutterSecureStorage();
class _DrawerMenuState extends State<DrawerMenu> {
#override
void initState() {
getInfo();
super.initState();
}
getInfo() async {
name = await storage.read(key: 'name');
email = await storage.read(key: 'email');
}
#override
Widget build(BuildContext context) {
Translations translations = Translations.of(context);
return Drawer(
backgroundColor: AppColors.secondaryColor,
child: SafeArea(
bottom: false,
child: Column(
children: [
ClipOval(
child: Container(
color: AppColors.primaryColor,
height: 60.0,
width: 60.0,
child: Center(
child: Text(
name![0],
style: TextStyle(
color: AppColors.secondaryColor,
fontSize: 30,
fontWeight: FontWeight.bold),
),
),
),
),
);
}
}
The first time I have this error: _CastError (Null check operator used on a null value)
But if I try to go next and re open drawer, so done!
I want to see the name in my drawer menu.
You have to set the state after setting the value in getInfo(). Also, you have to check if the name is null or not before accessing it.
getInfo() async {
name = await storage.read(key: 'name');
email = await storage.read(key: 'email');
setState({}); <-- add this
}
#override
Widget build(BuildContext context) {
Translations translations = Translations.of(context);
return Drawer(
backgroundColor: AppColors.secondaryColor,
child: SafeArea(
bottom: false,
child: Column(
children: [
ClipOval(
child: Container(
color: AppColors.primaryColor,
height: 60.0,
width: 60.0,
child: Center(
child: Text(
name==null? "" : name![0] ?? "", <-- update this
style: TextStyle(
color: AppColors.secondaryColor,
fontSize: 30,
fontWeight: FontWeight.bold),
),
),
),
),
);
}
}
It shows this error although I have added late and required in the Question class constructor. It's repeatedly shows
Exception caught by widgets library
The following LateError was thrown building _BodyBuilder:
LateInitializationError: Field 'ques' has not been initialized
Main class:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'QuestionsAnswers.dart';
void main() {
runApp(const Quizzler());
}
class Quizzler extends StatelessWidget {
const Quizzler({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.grey[900],
leading: Icon(Icons.games),
title: Text(
'Quizzler',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
color: Colors.white,
),
),
),
body: QuizPlay(),
),
),
);
}
}
class QuizPlay extends StatefulWidget {
const QuizPlay({Key? key}) : super(key: key);
#override
State<QuizPlay> createState() => _QuizplayState();
}
class _QuizplayState extends State<QuizPlay> {
List<Icon> score=[];// array of score icon
List<Questions>questionsAndAnswers=[
Questions(a:'Pakistan is an under developed country',b:true),
Questions(a:'Imran Khan is the Prime Minister of Pakistan',b:true),
Questions(a:'Y comes after U',b:false)
];
int questiontracker=0;// variable to increment of questions
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: Text(
questionsAndAnswers[questiontracker].ques,
style: TextStyle(
fontSize: 25.0,
color: Colors.white70,
),
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(10.0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.green),
),
onPressed: () {
//Yes button
bool answer=questionsAndAnswers[questiontracker].ans;
if (answer==true)
{
print('correct answer');
}
else
{
print('wrong answer ');
}
setState(() {
questiontracker++;
score.add(Icon(Icons.check,color: Colors.green,)) ;
});
},
child: Text(
'Yes',
style: TextStyle(
fontSize: 20.0,
),
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(10.0),
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red),
),
onPressed: () {
// No button
bool answer=questionsAndAnswers[questiontracker].ans;
if (answer==false)
{
print('correct answer');
}
else
{
print('wrong answer ');
}
setState(() {
questiontracker++;
score.add(Icon(Icons.close,color: Colors.red,)) ;
});
},
child: Text(
'No',
style: TextStyle(
fontSize: 20.0,
),
),
),
),
),
Row(
children: score,
),
],
);
}
}
###Question CLASS###
class Questions{
late String ques;
late bool ans;
Questions({required String a,required bool b})
{
a=ques;
b=ans;
}
}
make it
ques = a;
ans = b;
This stores the value on the right in the value on the left.
Your class constructor Questions is wrong, change it to:
class Questions{
late String ques;
late bool ans;
Questions({required String a,required bool b}) {
ques = a;
and = b;
}
}
What is the purpose of having your questions as a plain class? I'd suggest turning it into a module class which in turn should be
class Question
{
String? ques;
bool? ans;
Question({
this.ques, this.ans});
}
and when you want to initialize a question I'd suggest creating a list
List<Question> questions = [];
question.add(Question("question",true));
// add more as you wish
This will allow you to turn it into JSON which will enable you to maybe provide questions from an online database to the app without needing to update the app every time you want to add a question.
I am still relatively new to coding and only still learning dart. I have made multiple of the same button using classes, how do i customize each button individually, please can you help. Here is the code:
Button Code:
class JeffButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 9.0, 0.0, 0.0),
child: TextButton.icon(
onPressed: () => {},
icon: Column(
children: [
Icon(
Icons.add,
color: Colors.white,
size: 85,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'Label',
style: TextStyle(
color: Colors.white,
),
),
),
],
),
label: Text(
'', //'Label',
style: TextStyle(
color: Colors.white,
),
),
),
);
}
}
Other Code:
class home_buttons {
List<Widget> jeffButtons = [
JeffButton(),
JeffButton(),
JeffButton(),
JeffButton(),
JeffButton(),
JeffButton(),
];
}
You'll need to have properties on your class - That way, when you create instances of your button, you can pass different values as arguments to the constructor, and those different values can be used to customize each instance of the button.
So, for example, if you want to customize the title label, give your class a title property:
class JeffButton extends StatelessWidget {
final String title;
JeffButton({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 9.0, 0.0, 0.0),
child: TextButton.icon(
onPressed: () => {},
label: Text(
title, // Use the title property to set the label text,
style: TextStyle(
color: Colors.white,
),
),
),
);
}
}
Then you can set different titles for each button:
final continueButton = JeffButton(title: 'Continue');
final cancelButton = JeffButton(title: 'Cancel');
You can create constructors, here is an example for padding.
class JeffButton extends StatelessWidget {
EdgeInsetsGeometry padding;
JeffButton({this.padding = EdgeInsets.all(8)});
#override
Widget build(BuildContext context) {
return Padding(
padding: padding,
);
}
}
You can use it like:
JeffButton(padding: EdgeInsets.all(10));
I am making a login screen. I want to have the text: "By continuing you agree the xxx Terms & Conditions and Privacy Policy" where -terms and conditions- and -privacy policy- are buttons which when clicked on, navigate to two separate screens.
Is this possible in Flutter. Please take note that due to the length of the final text string, it could wrap onto more than one line depending on the screen size.
Really appreciate any help with this.
Carson
you can do it with RichText
Like this
class DoItWithRichText extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: RichText(
text: TextSpan(
text: "By continuing you agree the",
children:[
TextSpan(
text: " Terms and Conditions",
style: TextStyle(
color: Colors.blue
),
recognizer: TapGestureRecognizer()..onTap = () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => TermsAndConditions()))
),
TextSpan(
text: " and "
),
TextSpan(
text: "Privacy Policy",
style: TextStyle(
color: Colors.blue
),
recognizer: TapGestureRecognizer()..onTap = () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => PrivacyAndPolicy()))
)
],
style: TextStyle(
color: Colors.black,
fontSize: 13
)
),
),
),
),
);
}
}
You could use RawMaterialButton to do that:
class LongTextRowWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('By continuing you agree to the xxx '),
InlineTextButton(text: 'Terms and Conditions', function: _jumpToTermsAndConditions),
const Text(' and '),
InlineTextButton(text: 'Privacy Policy', function: _jumpToPrivacyPolicy),
],
);
}
}
class InlineTextButton extends StatelessWidget {
final String text;
final Function function;
InlineTextButton({this.text, this.function});
#override
Widget build(BuildContext context) {
return RawMaterialButton(
constraints: BoxConstraints(),
onPressed: function,
child: Text(
text,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryColor,
),
),
);
}
}
Hi In my App I have something like this.
where I have a dropdown which displaying 3 options, but is there any way I can select multiple options inside the dropdown in flutter? and to store the result of selected options inside the list?
or is it possible to do something like below in flutter?
Thanks.
Code:-
class CustomMultiselectDropDown extends StatefulWidget {
final Function(List<String>) selectedList;
final List<String> listOFStrings;
CustomMultiselectDropDown(
{required this.selectedList, required this.listOFStrings});
#override
createState() {
return new _CustomMultiselectDropDownState();
}
}
class _CustomMultiselectDropDownState extends State<CustomMultiselectDropDown> {
List<String> listOFSelectedItem = [];
String selectedText = "";
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Container(
margin: EdgeInsets.only(top: 10.0),
decoration:
BoxDecoration(border: Border.all(color: PrimeDentalColors.grey1)),
child: ExpansionTile(
iconColor: PrimeDentalColors.grey,
title: Text(
listOFSelectedItem.isEmpty ? "Select" : listOFSelectedItem[0],
style: GoogleFonts.poppins(
textStyle: TextStyle(
color: PrimeDentalColors.grey,
fontWeight: FontWeight.w400,
fontSize: 15.0,
),
),
),
children: <Widget>[
new ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.listOFStrings.length,
itemBuilder: (BuildContext context, int index) {
return Container(
margin: EdgeInsets.only(bottom: 8.0),
child: _ViewItem(
item: widget.listOFStrings[index],
selected: (val) {
selectedText = val;
if (listOFSelectedItem.contains(val)) {
listOFSelectedItem.remove(val);
} else {
listOFSelectedItem.add(val);
}
widget.selectedList(listOFSelectedItem);
setState(() {});
},
itemSelected: listOFSelectedItem
.contains(widget.listOFStrings[index])),
);
},
),
],
),
);
}
}
class _ViewItem extends StatelessWidget {
String item;
bool itemSelected;
final Function(String) selected;
_ViewItem(
{required this.item, required this.itemSelected, required this.selected});
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Padding(
padding:
EdgeInsets.only(left: size.width * .032, right: size.width * .098),
child: Row(
children: [
SizedBox(
height: 24.0,
width: 24.0,
child: Checkbox(
value: itemSelected,
onChanged: (val) {
selected(item);
},
activeColor: PrimeDentalColors.blue,
),
),
SizedBox(
width: size.width * .025,
),
Text(
item,
style: GoogleFonts.poppins(
textStyle: TextStyle(
color: PrimeDentalColors.grey,
fontWeight: FontWeight.w400,
fontSize: 17.0,
),
),
),
],
),
);
}
}
You could achieve that by using a custom widget as a child of the DropdownMenuItem, where the custom widget would need to be stateful so it can handle it's own state to show a check mark or something. And it should have it's own onTap method, so the DropdownMenuItem onTap won't trigger and select the option, dismissing the dropdown. You will also need to have an option to finalize the selection.
But I reccommend you to look another approach for this case for a better usability, like a dialog where you can select multiple options: Is there an equivalent widget in flutter to the "select multiple" element in HTML
You can use the following package
https://pub.dev/packages/multiselect
it has a dropdown based implementation instead of Dialog to show options.
PS: I needed this feature in a recent project and had to create my own widget. this is my implementation.