So I'm currently trying to implement some searching functionality to my ListView and this does work great actually. When I type in some letters it automatically shows me the right things (-> See Screenshot_Listview_1.png and Screenshot_Listview_2.png).
There is only one problem. I want the different texts from my listview to be clickable, so when I click on them a new ModalBottomSheet should appear.
For example: I'm searching for "Apple" and when I click on the text "Apple" a ModalBottomSheet opens and I can read some facts about apples.
I tried the onTap method and it works so far but I only managed to open the same BottomSheet.. But I need different BottomSheets depending on what I have tapped on.
This is what I got so far. Can you please help me out? I really don't know how to solve this problem. Thank you so much!!
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class GlossarScreen extends StatefulWidget {
#override
_GlossarScreenState createState() => _GlossarScreenState();
}
class _GlossarScreenState extends State<GlossarScreen> {
TextEditingController _textEditingController = TextEditingController();
List<String> glossarListOnSearch = [];
List<String> glossarList = [
'Apple',
'Orange',
'Banana',
'Grapefruit',
'Mango',
'Kiwi',
'Grapes',
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Glossar'),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
),
bottom: PreferredSize(
preferredSize: Size(0, 60),
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
child: Container(
//height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white60, Colors.white70],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
borderRadius: BorderRadius.circular(50),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: TextField(
textAlign: TextAlign.left,
onChanged: (value) {
setState(() {
glossarListOnSearch = glossarList
.where((element) => element
.toLowerCase()
.contains(value.toLowerCase()))
.toList();
});
},
controller: _textEditingController,
decoration: InputDecoration(
border: InputBorder.none,
errorBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.all(0),
hintText: 'Search'),
),
),
),
),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: _textEditingController.text.isNotEmpty &&
glossarListOnSearch.isEmpty
? Column(
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
child: Text(
'No results',
style: TextStyle(
fontFamily: 'Avenir',
fontSize: 22,
color: Color(0xff848484)),
),
),
)
],
)
: ListView.builder(
itemCount: _textEditingController.text.isNotEmpty
? glossarListOnSearch.length
: glossarList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_testFuction(context);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
child: Text(
_textEditingController.text.isNotEmpty
? glossarListOnSearch[index]
: glossarList[index],
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontFamily: 'Avenir'),
),
),
);
},
),
),
);
}
}
void _testFuction(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return Scaffold(
body: Text('This text should be dependent on what I have tapped on. If I tap on "Apple" a different ModalBottomSheep shall appear then when I press on "Banana".'),
);
},
);
}
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'package:stack_demo/models/FruitModel.dart';
class GlossarScreen extends StatefulWidget {
#override
_GlossarScreenState createState() => _GlossarScreenState();
}
class _GlossarScreenState extends State<GlossarScreen> {
TextEditingController _textEditingController = TextEditingController();
List<FruitModel> glossarListOnSearch = [];
List<FruitModel> glossarList = [];
#override
void initState() {
glossarList.add(FruitModel(id: 0, name: 'Apple', facts: 'Good for health'));
glossarList.add(
FruitModel(id: 1, name: 'Banana', facts: 'Banana is also for health'));
glossarList.add(
FruitModel(id: 2, name: 'Orange', facts: 'Orange good for health'));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Glossar'),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFBD23E), Color(0xffF6BE03)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
),
bottom: PreferredSize(
preferredSize: Size(0, 60),
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 10),
child: Container(
//height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white60, Colors.white70],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
borderRadius: BorderRadius.circular(50),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
child: TextField(
textAlign: TextAlign.left,
onChanged: (value) {
setState(() {
glossarListOnSearch = glossarList
.where((element) => element.name!
.toLowerCase()
.contains(value.toLowerCase()))
.toList();
});
},
controller: _textEditingController,
decoration: InputDecoration(
border: InputBorder.none,
errorBorder: InputBorder.none,
enabledBorder: InputBorder.none,
contentPadding: EdgeInsets.all(0),
hintText: 'Search'),
),
),
),
),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xffFEFDFD), Color(0xffBDBDB2)],
begin: Alignment.topLeft,
end: Alignment.bottomRight),
),
child: _textEditingController.text.isNotEmpty &&
glossarListOnSearch.isEmpty
? Column(
children: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 50, 0, 0),
child: Text(
'No results',
style: TextStyle(
fontFamily: 'Avenir',
fontSize: 22,
color: Color(0xff848484)),
),
),
)
],
)
: ListView.builder(
itemCount: _textEditingController.text.isNotEmpty
? glossarListOnSearch.length
: glossarList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
_textEditingController.text.isNotEmpty
? _testFuction(context, glossarListOnSearch[index])
: _testFuction(context, glossarList[index]);
},
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
child: Text(
_textEditingController.text.isNotEmpty
? glossarListOnSearch[index].name!
: glossarList[index].name!,
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontFamily: 'Avenir'),
),
),
);
},
),
),
);
}
}
void _testFuction(context, FruitModel model) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return Scaffold(
body: Text('${model.facts}'),
);
},
);
}
You can wrap the your Padding with a GestureDetector and add actions into the onTap method.
return GestureDetector(
onTap: () {
// TODO: Add actions onTap
},
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 15, 12, 15),
child: Text(
_textEditingController.text.isNotEmpty
? glossarListOnSearch[index] : glossarList[index],
style: TextStyle(
color: Colors.black, fontSize: 24, fontFamily: 'Avenir'),
),
);
);
You need give your _testFuction some content what depends on you tap to let the bottomsheet know what it should to show.Just likes:
return GestureDetector(
onTap:(){
_testFuction(context,glossarListOnSearch[index]);
}
...
)
void _testFuction(context, someContent) {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return Scaffold(
body: Text('This is $someContent bottomsheet'),
);
},
);
}
Related
Exception has occurred.
FlutterError setState() or markNeedsBuild() called during build.
This TextFormField widget cannot be marked as needing to build because the framework is already in the process of building widgets.
A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building.
This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was:
TextFormField
The widget which was currently being built when the offending call was made was:
Builder)
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:project_1_money_management/Screens/Home/home_screen.dart';
import 'package:project_1_money_management/db/transaction_db.dart';
import 'package:project_1_money_management/models/category_model.dart';
import 'package:project_1_money_management/update/update_category.dart';
import '../Screens/Adding_items/Widgets/date_picker.dart';
import '../db/category_db.dart';
import '../models/transactions_model.dart';
final purposecontroller = TextEditingController();
final amountcontroller = TextEditingController();
class UpdateScreen extends StatefulWidget {
final TransactionModel value;
const UpdateScreen({Key? key, required this.value}) : super(key: key);
#override
State<UpdateScreen> createState() => _UpdateScreenState();
}
class _UpdateScreenState extends State<UpdateScreen> {
CategoryType? type;
DateTime? _date;
CategoryModel? cat;
#override
// ignore: must_call_super
void initState() {
CategoryDB().refreshUI();
TransactionDB.instance.refresh();
super.initState();
amountcontroller.text = widget.value.amount.toString();
purposecontroller.text = widget.value.purpose;
_date = widget.value.date;
cat = widget.value.category;
type = widget.value.type;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 35, 32, 32),
body: SafeArea(
child: ListView(
children: [
Padding(
padding: const EdgeInsets.only(top: 40.0),
child: Text(
'Add Transactions',
style: GoogleFonts.inconsolata(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
const SizedBox(
height: 40,
),
Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8),
child: Card(
color: const Color.fromARGB(48, 175, 171, 171),
elevation: 60,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Padding(
padding: const EdgeInsets.only(top: 40.0, bottom: 40),
child: Column(
children: [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
primary: const Color.fromARGB(255, 255, 251,
253), //change background color of button
onPrimary: const Color.fromARGB(
255, 56, 120, 204), //change text color of button
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
elevation: 15.0,
),
onPressed: () {
selectDates(context);
},
icon: const Icon(Icons.calendar_month),
label: Text(
'${_date!.day}/${_date!.month}/${_date!.year}',
),
),
const SizedBox(
height: 10,
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10),
child: Row(
children: [
CateogryUpdate(cats: cat!, types: type!),
],
)),
Form(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: amountcontroller,
decoration: InputDecoration(
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: purposecontroller,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
vertical: 40.0, horizontal: 10.0),
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
],
)),
ClipRRect(
borderRadius: BorderRadius.circular(19),
child: Stack(
children: <Widget>[
Positioned.fill(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
Color.fromARGB(255, 81, 185, 67),
Color.fromARGB(255, 32, 188, 32),
Color.fromARGB(255, 52, 181, 32),
],
),
),
),
),
TextButton(
style: TextButton.styleFrom(
padding: const EdgeInsets.only(),
primary:
const Color.fromARGB(255, 247, 247, 247),
textStyle: GoogleFonts.inconsolata(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
onPressed: () async {
await update(
widget.value.id!,
amountcontroller.text,
);
},
child: const Text(' Update '),
),
],
),
),
const SizedBox(
height: 14,
),
],
),
),
),
),
],
),
),
);
}
selectDates(BuildContext context) async {
selected = await showDatePicker(
context: context,
initialDate: _date!,
firstDate: DateTime(2021),
lastDate: DateTime.now(),
);
if (selected != null && selected != _date!) {
setState(() {
_date = selected!;
});
}
}
update(String id, String amt) async {
final _update = TransactionModel(
amount: double.tryParse(amt)!,
purpose: purposecontroller.text,
category: cat!,
date: _date!,
type: type,
id: id,
);
await TransactionDB.instance.updateTransact(_update);
Navigator.of(context)
.push(MaterialPageRoute(builder: (route) => const ScreenHome()));
}
}
Try below code before seState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (selected != null && selected != _date!) {
setState(() {
_date = selected!;
});
}
});
I want to implement OTP verification screen without any package.
when i entered the number it should move to next input field
I m using this code in my current project take a refrence this will help
class Otp extends StatefulWidget {
final String? phnNumber;
final String ? code;
String? from;
Otp({Key ?key, this.phnNumber, this.from, this.code}) : super(key:
key);
#override
_OtpState createState() => _OtpState();
}
class _OtpState extends State<Otp> {
double ? height ;
double ? width;
TextEditingController ? contrller1;
TextEditingController ? contrller2;
TextEditingController ? contrller3;
TextEditingController ? contrller4;
SendOtpRequest resend = SendOtpRequest();
SharedPreferences ? prefs;
getSharedPreferences () async
{
prefs = await SharedPreferences.getInstance();
}
String Code = "";
#override
void initState() {
// TODO: implement initState
super.initState();
contrller1 = TextEditingController();
contrller2 = TextEditingController();
contrller3 = TextEditingController();
contrller4 = TextEditingController();
getSharedPreferences();
}
#override
Widget build(BuildContext context) {
height= MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.height;
final verifyprovider = Provider.of<PostDataProvider>(context);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
toolbarHeight:height! * 0.07802345,
titleSpacing: 0,
backgroundColor: HexColor("#18263d"),
automaticallyImplyLeading: false,
leading: Padding(
padding: const EdgeInsets.only(left: 8.0,),
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
color: Colors.transparent,
child: Image.asset("assets/images/back_ic-1.png")),
),
),
// SizedBox(width: width!*0.001234,),
title:Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: height!/15,
width: height!/15,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 2,
color:HexColor("#fc4f00"),
)),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Container(
height: height!/11,
width: height!/11,
decoration: BoxDecoration(
image: const DecorationImage(
image:
AssetImage("assets/images/home_logo.png"),
fit: BoxFit.fill
),
shape: BoxShape.circle,
border: Border.all(
width: 1,
color:HexColor("#fc4f00"),
)),
),
),
),
SizedBox(width: width! * 0.04234,),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text("Verification",
style: GoogleFonts.oswald(fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: width! * 0.03345
),),
),
],
) ,
),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 32),
child: Column(
children: [
Text("We have send verification code on your mobile number",
style: GoogleFonts.oswald(fontStyle: FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#8b8b8b")),
),
SizedBox(height: height!/38,),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_textFieldOTP(first: true, last: false,controllerr:
contrller1),
_textFieldOTP(first: false, last: false,controllerr:
contrller2),
_textFieldOTP(first: false, last: false,controllerr:
contrller3),
_textFieldOTP(first: false, last: true, controllerr:
contrller4),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap:() {
resend.phoneNumber= widget.phnNumber;
resend.countryCode = widget.code;
verifyprovider.resendOtp(context,
jsonEncode(resend));
},
child: Text("Resend OTP?",
style: GoogleFonts.oswald(fontStyle:
FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#fc4f00")),
),
),
),
SizedBox(height: height!/28,),
GestureDetector(
onTap: (){
if(contrller1!.text.isNotEmpty&&
contrller2!.text.isNotEmpty&&contrller3!.
text.isNotEmpty&&contrller4!.text.isNotEmpty){
verifyOtpRequest verify = verifyOtpRequest();
verify.phoneNumber = widget.phnNumber;
verify.otp=
contrller1!.text+contrller2!.
text+contrller3!.text+contrller4!.text;
verifyprovider.otpVerification(context,
jsonEncode(verify), widget.from);
}else{
CommonUtils.showToast(msg: "Please fill all the
fields ");
}
},
child: Container(
height: height!/18,
width: width,
decoration: BoxDecoration(
color: HexColor("#fc4f00"),
borderRadius: BorderRadius.circular(10)
),
child: Center(
child: Text("Verify",style: TextStyle(
color: Colors.white,
fontSize: width!*0.02345
),),
)
),
),
],
),
],
),
),
),
);
}
Widget _textFieldOTP({bool ? first, last,
TextEditingController ?
controllerr}) {
return Container(
height:height!/12 ,
child: AspectRatio(
aspectRatio: 1.0,
child: TextField(
controller: controllerr,
autofocus: true,
onChanged: (value) {
if (value.length == 1 && last == false) {
FocusScope.of(context).nextFocus();
}
if (value.length == 0 && first == false) {
FocusScope.of(context).previousFocus();
}
},
showCursor: false,
readOnly: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
counter: Offstage(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
),
),
),
);
}
}
When the length of the input data reaches one, you will have to change the text field focus node.
For Example
If you are in the first field, and you enter a number field one focus should be lost, and field two should be in focus. This can be done, by requestFocus.
This article will of help for you: Flutter Focus
I'm a Flutter newbie. I had a trouble with my own searchbar in the draggable bottom sheet. I created a draggable bottom sheet to search for information when clicking to the search field from the main screen. I added a searchbar and a listview in the sheet but the searchbar did not work properly. It could not instantly filter out the data, it just showed the results when closing the keyboard. Anyone helps me please.
class AddPersonalInfo extends StatefulWidget {
final String mail, password;
const AddPersonalInfo({Key? key, required this.mail, required this.password})
: super(key: key);
#override
_AddPersonalInfoState createState() => _AddPersonalInfoState();
}
class _AddPersonalInfoState extends State<AddPersonalInfo> {
TextEditingController _search = TextEditingController();
List<City> cites = [
City(id: 1, code: "HN", name: "Ha Noi"),
City(id: 2, code: "HCM", name: "Ho CHi Minh"),
City(id: 3, code: "DN", name: "Da Nang"),
City(id: 4, code: "HP", name: "Hai Phong"),
City(id: 5, code: "CT", name: "Can Tho"),
City(id: 6, code: "DNN", name: "Dong Nai"),
City(id: 7, code: "KH", name: "Khanh Hoa"),
City(id: 8, code: "PY", name: "Phu Yen"),
City(id: 9, code: "NT", name: "Nha Trang"),
City(id: 10, code: "VL", name: "Vinh Long"),
City(id: 11, code: "HD", name: "Hai Duong"),
City(id: 12, code: "BD", name: "Binh Duong")
];
City? selected;
List<City> foundCity = [];
#override
void initState() {
super.initState();
setState(() {
foundCity = cites;
});
}
#override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.of(context).size;
Orientation orientation = MediaQuery.of(context).orientation;
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: LayoutBuilder(builder: (context, snapshot) {
if (snapshot.maxWidth <= screenSize.width &&
orientation == Orientation.portrait) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0,
30 * screenScale(context), 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
Container(
alignment: Alignment.topCenter,
padding: EdgeInsets.only(
bottom: 20 * screenScale(context)),
child: introText("Create account", context),
),
backBtn(context),
],
),
mainText(
"Let's create an account to grab all latest gadgets and enjoy the best experiences.",
context),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [_fnameField(), _lnameField()],
),
_phoneField(),
GestureDetector(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => buildSheet(),
),
child: Container(
height: 50,
margin: EdgeInsets.only(top: 15),
padding: EdgeInsets.only(left: 10, right: 7),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(7),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
selected == null
? Text("Select city",
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade700))
: Text("${selected!.name}",
style: TextStyle(
fontSize: 16, color: Colors.black)),
Icon(Ionicons.chevron_down_outline, size: 24)
],
),
),
)
],
),
),
);
} else {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0,
30 * screenScale(context), 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [],
),
),
);
}
}),
),
),
);
}
Widget buildSheet() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: DraggableScrollableSheet(
initialChildSize: 0.9,
builder: (_, controller) => Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
children: [
Divider(
thickness: 4,
color: Colors.grey.shade300,
endIndent: 170,
indent: 170,
),
Padding(
padding: EdgeInsets.only(bottom: 15, top: 5),
child: Text("Select city",
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.only(bottom: 10),
child: TextFormField(
controller: _search,
keyboardType: TextInputType.name,
style: TextStyle(fontSize: 16 * fontScale(context)),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(7 * screenScale(context))),
contentPadding:
EdgeInsets.only(top: 10 * screenScale(context)),
hintText: 'Search',
prefixIcon: Icon(Ionicons.search_outline),
),
onChanged: (value) {
setState(() {
foundCity = cites
.where(
(city) => city.name.toLowerCase().contains(value))
.toList();
});
},
),
),
Expanded(
child: foundCity.length > 0
? ListView.builder(
itemCount: foundCity.length,
itemBuilder: (context, index) {
final city = foundCity[index];
return ListTile(
title: Text(city.name),
onTap: () {
Navigator.of(context).pop();
setState(() {
selected = city;
});
},
);
},
)
: Padding(
padding: EdgeInsets.only(top: 50),
child: Text(
"No data.",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
}
}
Ok I checked you code, and it seems you missed a small issue, when you're creating a showModalBottomSheet, it's no longer part of your stateful widget
I've adjusted the code so that it works now:
Widget buildSheet(BuildContext context) {
return StatefulBuilder(builder: (BuildContext context, setState) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).pop();
},
child: DraggableScrollableSheet(
initialChildSize: 0.9,
builder: (_, controller) => Container(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
),
),
child: Column(
children: [
Divider(
thickness: 4,
color: Colors.grey.shade300,
endIndent: 170,
indent: 170,
),
Padding(
padding: EdgeInsets.only(bottom: 15, top: 5),
child: Text("Select city",
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold)),
),
Padding(
padding: EdgeInsets.only(bottom: 10),
child: TextFormField(
controller: _search,
keyboardType: TextInputType.name,
style: TextStyle(fontSize: 16),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(7)),
contentPadding: EdgeInsets.only(top: 10),
hintText: 'Search',
prefixIcon: Icon(Icons.access_alarm),
),
onChanged: (value) {
setState(() {
foundCity = cites
.where((city) =>
city.name!.toLowerCase().contains(value))
.toList();
});
},
),
),
Expanded(
child: foundCity.isNotEmpty
? ListView.builder(
itemCount: foundCity.length,
itemBuilder: (context, index) {
final city = foundCity[index];
return ListTile(
title: Text(city.name!),
onTap: () {
Navigator.of(context).pop();
setState(() {
selected = city;
});
},
);
},
)
: Padding(
padding: EdgeInsets.only(top: 50),
child: Text(
"No data.",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
});
}
I want to implement Tab Bar in my application having length 2 named "Need Help" and "Help Requests". In "Need Help" tab, I want my first container (i.e. Upload data to Firestore Database) and in "Help Requests" tab, I want my second container (i.e. Retrieve data from Firestore Database). I am new to flutter and will be very much grateful to you if you can help me.
Source code:
import 'package:chat_app/group_chats/group_info.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import '../global_data.dart';
class FinancialRoom extends StatelessWidget {
final String groupChatId, groupName;
FinancialRoom({required this.groupName, required this.groupChatId, Key? key})
: super(key: key);
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController amountValue = TextEditingController();
void onSend() async {
Map<String, dynamic> data = {
"amount": amountValue.text,
"sendBy": _auth.currentUser!.displayName,
"type": "text",
"time": FieldValue.serverTimestamp(),
};
amountValue.clear();
await _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.add(data);
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text(groupName),
actions: [
IconButton(
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => GroupInfo(
groupName: groupName,
groupId: groupChatId,
),
),
),
icon: Icon(Icons.more_vert)),
],
),
body: SafeArea(
child: ListView(padding: EdgeInsets.all(20.0), children: [
Container(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 10.0,
),
TextFormField(
controller: amountValue,
decoration: InputDecoration(
hintText: 'Enter the amount you want',
labelText: 'Enter the amount you want',
prefixIcon: Icon(Icons.account_balance_wallet_outlined),
enabledBorder: kEnabledBorder,
focusedBorder: kFocusedBorder,
errorBorder: kErrorBorder,
focusedErrorBorder: kErrorBorder,
),
onTap: () {
},
// The validator receives the text that the user has entered.
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the amount you want';
}
return null;
},
),
SizedBox(
height: kInputSpacing,
),
SizedBox(
width: double.infinity,
child: FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.blue,
textColor: Colors.white,
padding: EdgeInsets.only(top: 16.0, bottom: 16.0),
onPressed: onSend,
child: Text(
'SEND',
style: kButtonTextStyle,
),
),
),
],
),
),
Container(
height: size.height / 1.27,
width: size.width,
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('groups')
.doc(groupChatId)
.collection('chats')
.orderBy('time')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
Map<String, dynamic> data =
snapshot.data!.docs[index].data()
as Map<String, dynamic>;
return messageTile(size, data);
},
);
} else {
return Container();
}
},
),
),
]),
),
);
}
Widget messageTile(Size size, Map<String, dynamic> data) {
return Builder(builder: (_) {
if (data['type'] == "text") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: Colors.blue,
),
child: Column(
children: [
Text(
data['sendBy'],
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
SizedBox(
height: size.height / 200,
),
Text(
data['amount'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
)),
);
} else if (data['type'] == "img") {
return Container(
width: size.width,
alignment: data['sendBy'] == _auth.currentUser!.displayName
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 14),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
height: size.height / 2,
child: Image.network(
data['amount'],
),
),
);
} else if (data['type'] == "notify") {
return Container(
width: size.width,
alignment: Alignment.center,
child: Container(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.black38,
),
child: Text(
data['message'],
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
);
} else {
return SizedBox();
}
});
}
}
It's very straightforward to implement a simple TabBar in your app. All you need is a TabController and two widgets called TabBar and TabBarView. Here is a simple example:
DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(tabs: [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
]),
),
body: TabBarView(children: [
// Tab 1
Container(color: Colors.red),
// Tab 2
Container(color: Colors.blue),
]),
),
);
Now all you need to do is to replace children inside TabBarView with whatever you want to display.
I have designed a ui using flutter first I checked it in my phone which is Moto g5s plus the ui looks goods and I occupies entire screen but when I open the same app in iPhone 11 pro max I am getting a huge empty space but when I open it in iPhone 8 then the ui looks fine as it is in Moto g5s plus. I want to know how to have same consistent ui across different device sizes in flutter?
class Login extends StatefulWidget
{
#override
State<StatefulWidget> createState() {
return LoginState();
}
}
class LoginState extends State<Login>
{
String _email,_password;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState()
{
super.initState();
}
Widget _progressBar(AuthProvider authProvider)
{
//print("progress"+authProvider.progressStatus.toString());
return Visibility(
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: authProvider.progressStatus,
child: Container(
child: Center(
child: SizedBox(
width: 60,
height: 60,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0), //(x,y)
blurRadius: 1.0,
),
],
),
),
Center(
child: CircularProgressIndicator(),
)
],
),
)
)
),
);
}
Widget _emailTextField(AuthProvider authProvider)
{
return TextFormField(
onChanged: (String value){
authProvider.checkEmail(value);
},
decoration: InputDecoration(
labelText: "Email id",
errorText: authProvider.emailStatus ? null : "Enter a valid email",
border: OutlineInputBorder(
borderRadius: new BorderRadius.circular(32.0),
)
),
keyboardType: TextInputType.emailAddress,
validator: (String value){
if(value.isEmpty || !RegExp(r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?").hasMatch(value))
{
return "Enter a valid email";
} else {
return null;
}
},
onSaved: (String value){
_email = value;
},
);
}
Widget _passwordTextField(AuthProvider authProvider)
{
//print("pass check "+authProvider.passStatus.toString());
return TextFormField(
decoration: InputDecoration(labelText: "Password",
filled: true,
errorText: authProvider.passStatus ? null : "Password is too short",
border: OutlineInputBorder(
borderRadius: new BorderRadius.circular(32.0),
),
fillColor: Colors.white
),
keyboardType: TextInputType.text,
obscureText: true,
onChanged: (String value){
authProvider.checkPassword(value);
},
validator: (String value){
if(value.isEmpty)
{
return "Enter a valid password";
}
else if(value.length < 8)
{
return "Password is too short";
}
else
{
return null;
}
},
onSaved: (String value){
_password = value;
},
);
}
void _login(AuthProvider authProvider)
{
if(!_formKey.currentState.validate())
{
return;
}
_formKey.currentState.save();
Map<String,String> map = new Map();
map['email'] = _email;
map['password'] = _password;
authProvider.login(map);
}
#override
Widget build(BuildContext context) {
final authProvider = Provider.of<AuthProvider>(context);
if(authProvider.loginStatus != null && authProvider.loginStatus != "success")
{
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text('${authProvider.loginStatus}',
style: TextStyle(color: Colors.black),),
backgroundColor: Color(0xFFe5e5e5),
));
}
return Scaffold(
key: _scaffoldKey,
body: Stack(
children: <Widget>[
SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
margin: EdgeInsets.fromLTRB(0, 48, 0, 0),
child: Text(
"Take Orders",
style: TextStyle(color: Theme.of(context).primaryColorDark,fontSize: 20),
),
),
Container(
child: Text(
"Track the Best Selling Items",
style: TextStyle(color: Theme.of(context).primaryColor,fontSize: 16),
),
),
Container(
margin: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: SvgPicture.asset('assets/images/undraw_booking.svg',width: 100.0,height: 280.0,),
),
Container(
margin: EdgeInsets.fromLTRB(16, 0, 0, 0),
child:Row(
children: <Widget>[
Text(
"Login To ",
style: TextStyle(color: Colors.black,fontSize: 20)
),
Text(
"Take orders",
style: TextStyle(color: Theme.of(context).primaryColorDark,fontSize: 20),
)
],
),
),
Container(
margin: EdgeInsets.fromLTRB(16, 8, 16, 0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
//_emailTextField(_loginBloc),
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: _emailTextField(authProvider),
),
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: _passwordTextField(authProvider),
),
Container(
margin: EdgeInsets.fromLTRB(0, 8, 0, 0),
child: Align(
alignment: Alignment.centerLeft,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(32.0)
),
padding: EdgeInsets.fromLTRB(64, 12, 64, 12),
color: Theme.of(context).accentColor,
textColor: Colors.white,
child: Text(
"Login",
),
onPressed: (){
_login(authProvider);
},
) ,
),
),
],
),
),
),
],
),
)
),
_progressBar(authProvider),
],
),
);
}
}
images of emulator
without seeing code i cannot advise you proper solution however, i guess that tricks will solve your many issues.
instead of using fixed values
try to use
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
you can also use them like
height: MediaQuery.of(context).size.height * 0.80, // 80% of screen height
width: MediaQuery.of(context).size.width * 0.35, // 35% of screen width
Hope will help you