I'm working on an app and in the begin I extracted a widget to make my code clean and organized..
When I give the constracter the parameters, it force me to add "required" keyword, which it wasn't necessary befor!!
How can I ignore it?
class NewWidget extends StatelessWidget {
NewWidget({this.text, this.onPress});
final String text;
final Function onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text,
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: onPress(),
),
),
);
}
}
And this is the notification I had for it..
The parameter 'text' can't have a value of 'null' because of its type, but the implicit default value is 'null'. Try adding either an explicit non-'null' default value or the 'required' modifier.
It comes from Dart's nullsafety, your two parameters are defined as non-nullable variables but they are optional inside of your constructor which means the compiler cannot infer if the type should be String or String? for your text variable and cannot determine if onPress is a Function or Function?.
You either need to pass your parameters as required or declare them as nullable and check if they are null or not. Here is a sample for both cases:
Adding required
class NewWidget extends StatelessWidget {
NewWidget({required this.text, required this.onPress});
final String text;
final Function onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text,
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: onPress,
),
),
);
}
}
Using nullable parameters
class NewWidget extends StatelessWidget {
NewWidget({this.text, this.onPress});
final String? text;
final Function? onPress;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Card(
color: Colors.blueAccent,
elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: TextButton(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 5.0, horizontal: 100.0),
child: Text(
text ?? "text String is null",
style: TextStyle(
fontSize: 32.0,
color: Colors.white,
),
),
),
onPressed: () => onPress!(),
),
),
);
}
}
Related
I want to create a Card that is reusable with Image. Am I on the right track in the new type of Card? I do not know how to put the Image on the card. all the question regarding the reusable widget card type in stackoverflow and youtube seems old and i dont know if it is truly working in the newer version.
Prototype Figma of My vision of Card in the HomePage
Here is the example for the clarifcation of the image on the background
this is the previous code that I want to be scrapped because they are too many.
Container(
padding: const EdgeInsets.all(8),
color: const Color.fromARGB(255, 75, 175, 78),
child: Center(
child: TextButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
const SecondPage(
plantname: 'Bell Pepper')));
},
child: const Text(
"Bell Pepper",
style: TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),
)),
)),
This the new type of Card that I want to be the reusable. But I dont know how to put the image and make it better.
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/cli_commands.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final Image imageOfPlant;
const ListViewCard(
{super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 150,
height: 200,
child: Text(title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),// textstyle
),),//text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
Make a asset folder in your project like this
Add you image(jpeg, png or other) to this folder
Go to pubspec.yaml & add your asset path
To add assets to your application, add an assets section, like this:
assets:
- assets/
Make this changes in your ListViewCard widget:
import 'package:flutter/material.dart';
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlant; //Change to String
const ListViewCard(
{super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset(
imageOfPlant,
height: 200,
width: 150,
fit: BoxFit.cover,
),
SizedBox(
width: 150,
height: 50,
child: Center(
child: Text(title,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)),// textstyle
),
),),//text //SizedBox
], // <widget>[]
), // column
), //inkwell
); // card
}
}
Use your card
ListViewCard(
title: 'Lotus', onTap: () {}, imageOfPlant: 'assets/image.jpg')
OUTPUT:
You can use Stack widget for this,
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final Image imageOfPlant;
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlant,
});
#override
Widget build(BuildContext context) {
return Card(
color: const Color.fromARGB(255, 75, 175, 78),
elevation: 0,
margin: const EdgeInsets.all(8),
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
//sizedBox of the card
child: Stack(
children: [
Positioned.fill( // or positioned with top.left,bottom,right
child: imageOfPlant,
),
Align(
alignment: Alignment.bottomCenter,//based on your need
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 150,
height: 200,
child: Text(
title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
), //text //SizedBox
], // <widget>[]
),
),
],
), // column
), //inkwell
); // card
}
}
Also GridTile has similar look.
Instead of pass image widget inside constructor, pass its path, here I use asset image you can also network image too. Try this:
class ListViewCard extends StatelessWidget {
final String title;
final void Function()? onTap;
final String imageOfPlantPath;
const ListViewCard({
super.key,
required this.title,
required this.onTap,
required this.imageOfPlantPath,
});
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 2,
),
borderRadius: BorderRadius.all(Radius.circular(20))),
child: InkWell(
splashColor: Colors.lightGreenAccent.withAlpha(30),
onTap: onTap,
child: Container(
height: 300,
width: 150,
clipBehavior: Clip.antiAliasWithSaveLayer,
alignment: Alignment.bottomCenter,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(imageOfPlantPath),
fit: BoxFit.cover),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: LayoutBuilder(builder: (context, constraints) {
return Container(
alignment: Alignment.center,
width: constraints.maxWidth,
height: constraints.maxHeight * 0.5,
decoration: BoxDecoration(
color: Color.fromARGB(255, 75, 175, 78),
border: Border(
top: BorderSide(
color: Colors.black,
width: 2,
),
),
),
child: Text(
title,
style: const TextStyle(
fontSize: 19,
fontFamily: 'RobotoMedium',
color: Color(0xffeeeeee)), // textstyle
),
);
}), //text //S,
),
), //inkwell
); // card
}
}
Note: inside LayoutBuilder you can play with height and do what you want, I set half of the image here.
Instead of using Image as DecorationImage, you can use Stack that #YeasinSheikh said blew.
I do a stateless widget category_card which is going to use four times.
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class CategoryCard extends StatelessWidget {
final String svgSrc;
final String title;
final VoidCallback press;
const CategoryCard(
{Key? key,
required this.svgSrc,
required this.title,
required this.press})
: super(key: key);
#override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(13),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(13),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.15),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: press,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
const Spacer(),
SvgPicture.asset(
svgSrc,
height: 100,
width: 100,
),
const Spacer(),
Text(
title,
style: Theme.of(context).textTheme.subtitle1!.copyWith(
fontWeight: FontWeight.w800,
fontSize: 15,
color: Colors.blueGrey[900]),
),
],
),
),
),
),
),
),
);
}
}
My output is alright, but the text part for long text such as medical condition the word is not align with the shorter word such as allergy. What can I do so that no matter how long the text all will have same starting alignment?
enter image description here
you can use Column property for making all it's item at the same point
Column(
crossAxisAlignment: CrossAxisAlignment.start, // or you can change it depends your need
children: [
I made a list of class OriginDestination, i.e. _allCities in my file. I then assigned all values in filteredCities and cities in initState.
Then I made a function runFilter which would take keyword from TextField and filter the results accordingly and save them to resultCities. Then I am using resultCities to display the information in ListView.builder. But the problem is, the list is not filtering according to the keyword i am searching.
Also, it would be appreciated if you can suggest a better way of using parameter cities, i.e. I don't think that passing the cities as parameter through state's constructor is a good practice.
Here is the code -
import 'package:flutter/material.dart';
import 'package:passenger_flutter_app/models/new_city.dart';
import 'package:passenger_flutter_app/models/origin_destination.dart';
import 'package:passenger_flutter_app/utils/colors.dart';
class SelectionScreen extends StatefulWidget {
List<OriginDestination>? cities;
SelectionScreen({this.cities});
#override
_SelectionScreenState createState() => _SelectionScreenState(cities);
}
class _SelectionScreenState extends State<SelectionScreen> {
final List<OriginDestination>? _allCities;
_SelectionScreenState(this._allCities);
bool originSelected=false;
List<OriginDestination>? resultCities = [];
List<OriginDestination>? filteredCities = [];
void getCitiesFromResponse() {
/*for(var city in _allCities!) {
cities!.add(city.origin!);
}*/
filteredCities=_allCities;
resultCities=_allCities;
}
#override
initState() {
// at the beginning, all users are shown
getCitiesFromResponse();
super.initState();
}
void _runFilter(String enteredKeyword) {
if (enteredKeyword.isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
filteredCities = _allCities;
} else {
filteredCities = _allCities!
.where((city) =>
city.origin!.name!.toLowerCase().contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
#override
void setState() {
resultCities=filteredCities;
}
}
#override
Widget build(BuildContext context) {
var originSelected;
return SafeArea(
child: Scaffold(
backgroundColor: const Color(0xffEEEDEF),
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width*0.8,
),
Row(
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
color: Colors.orange,
onPressed: () {
Navigator.pop(context);
},
),
Column(
children: [
originSelected==true ? Container(
child: Text(''),
) :
Container(
width: MediaQuery.of(context).size.width * 0.85,
height: 50.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(5.0)),
border: Border.all(color: colorAccent)),
child: TextField(
decoration: InputDecoration(
hintText: "Enter Origin",
border: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 10.0),
hintStyle: TextStyle(
fontSize: 15.0,
color: Colors.grey[500],
),
),
onChanged: (value) {
_runFilter(value);
},
),
),
],
),
],
),
],
),
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: EdgeInsets.only(
left: MediaQuery.of(context).size.width * 0.04, top: 3.0),
child: Text(
'Popular Searches:',
style: TextStyle(
color: popUpLightTextColor,
fontSize: MediaQuery.of(context).size.width * 0.035),
),
),
),
Expanded(
child: ListView.builder(
itemCount: resultCities!.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(
left: 18.0, top: index==0 ? 29.0 : 15.0, bottom: 15.0),
child: InkWell(
onTap: () {
print(resultCities?[index].origin!.name);
/*setState(() {
widget.city=filteredCities[index]['city'];
print("Changed to - ");
//print(widget.city);
Navigator.pop(context);
});*/
},
child: Text(
resultCities?[index].origin!.name??"No name",
style: const TextStyle(
color: darkText,
fontSize: 15.0,
fontWeight: FontWeight.normal,
),
),
),
);
},
),
),
],
),
),
);
}
}
seems like you defined the setState function instead of calling it, so instead of:
#override
void setState() {
resultCities=filteredCities;
}
write:
setState(() {
resultCities=filteredCities;
});
Why are you overriding the setState. You also pass the call back as argument in the setState.
You should be call setState on a trigger, like a gesture or button:
setState(() => resultCities = filteredCities);
I was trying to make this page but this design in last is not shifting to the last bottom, I've tried padding but this doesn't look good and also I've tried positioned widget but it is showing some error please someone tell how I can shift that design to bottom last
this is my git repo: https://github.com/cryptic-exe/Otp_verfication
this is my code:
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String dropdownValue = 'English';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Column(
children: [
Icon(
Icons.photo_outlined,
size: 100.0,
),
Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Please Select Your Language',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Text(
'You can change the language \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t at any time',
style: TextStyle(
fontWeight: FontWeight.w300, fontSize: 15.0),
),
),
Padding(
padding: const EdgeInsets.only(top: 9.0),
child: Container(
width: 200,
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(
border: Border.all(),
),
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_drop_down_outlined),
isExpanded: true,
iconSize: 30,
elevation: 16,
style: const TextStyle(color: Colors.black),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>[
'English',
'Hindi',
'French',
'Spanish',
'Russian',
'Arabic'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
fontSize: 20,
letterSpacing: 0.9,
fontWeight: FontWeight.w300),
),
);
}).toList(),
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
width: 200,
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
decoration: BoxDecoration(
color: Colors.deepPurple,
border: Border.all(),
),
child: TextButton(
onPressed: () {},
child: Text(
'NEXT',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.w400,
letterSpacing: 0.9),
),
),
),
),
],
),
),
Stack(
children: [
Positioned.fill(
child: Container(
width: 393,
child: Image(
image: AssetImage('Images/design2.png'),
fit: BoxFit.fill,
),
),
),
Positioned(
child: Container(
width: 393,
child: Image(
image: AssetImage('Images/design1.png'),
colorBlendMode: BlendMode.overlay,
fit: BoxFit.fill,
),
),
),
],
),
],
),
);
}
}
you can use bottomSheet to place your image.
Add the below code inside your Scaffold
bottomSheet: Container(
width: double.infinity,
child: Image(
image: AssetImage('Images/design1.png'),
colorBlendMode: BlendMode.overlay,
fit: BoxFit.fill,
),
),
You can also use a spacer to push the content down
https://api.flutter.dev/flutter/widgets/Spacer-class.html
On mobile so I cannot give snippet
I'm trying to get the splash to match the same shape as my Container that has a FlatButton as its child.
When pressed, the splash currently fills a different shape as shown here:
My code for the widget is below:
import 'package:flutter/material.dart';
class RoundedButton extends StatelessWidget {
const RoundedButton( {this.buttonColor, this.buttonTitle, #required this.onPressed});
final Color buttonColor;
final String buttonTitle;
final Function onPressed;
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 16.0),
height: 42.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: buttonColor,
),
child: FlatButton(
onPressed: onPressed,
child: Text(
buttonTitle,
style: TextStyle(
color: Colors.white
),
),
),
);
}
}
You can use ClipRRect widget which clips the underlying widget with rounded corners and by using borderRadius property and passing same radius as of parent widget ie, Container, ie, wrap the FlatButton with ClipRRect to achieve desired effect. Sample working code below:
body: Container(
margin: EdgeInsets.symmetric(vertical: 16.0),
height: 42.0,
width: 200.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: Colors.red,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(30),
child: FlatButton(
onPressed: () {
print('pressed');
},
child: Text(
'Send',
style: TextStyle(
color: Colors.white
),
),
),
)
),
Hope this answers your question.