textbox inside bottomsheet in flutter - flutter

I have a bottomsheet which looks like below
It has a text box inside it when I cliked on that I am getting something like
my text box is completely covered by the keyboard.
is there any way to solve this?
below is code
Widget buildSupportingWidget(
Map<String, Object> items, StateSetter setState) {
if (items['type'] == "string") {
dispName = items['displayName'];
return Container(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 27.0,
vertical: 16.0,
),
child: new TextFormField(
style: new TextStyle(fontSize: 12.0, color: Colors.black),
decoration: new InputDecoration(
labelText: "Enter value",
),
keyboardType: TextInputType.text,
controller: filterTextFieldData,
)));
}
return null;
}
I am calling above widget inside the bottomshhet.
Thanks

Last week I faced same issue I solved it by create stateful bottom sheet and add Container with height last widget of Column
Open bottom sheet
InkWell(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return ModalBottomSheet(
);
});
})
Stateful bottom sheet
class ModalBottomSheet extends StatefulWidget {
#override
_ModalBottomSheetState createState() => _ModalBottomSheetState();
}
class _ModalBottomSheetState extends State<ModalBottomSheet>
with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
double keyboardHeight = MediaQuery.of(context).viewInsets.bottom;
// TODO: implement build
return Wrap(
children: <Widget>[
Container(
margin:
EdgeInsets.only(left: 10.0, right: 10.0, top: 15.0, bottom: 15.0),
child: Column(
children: <Widget>[
Widgets(),
Container(
height: keyboardHeight ,
)
]
)
)
],
);
}
}
also Keyboard Visibility package help me to check keyboard open or not

Related

Overflow when using viewinsets in a modalButtomSheet

Problem:
I'm using MediaQuery and viewInsets to add Padding, when the user
triggers the keyboard in a modalBottomSheet.
It looks OK, but I get a message about overflow
When I draw down the modalBottomSheet manually, I can see the overflow happening behind the sheet.
Code first, then screenshots:
This is the GestureDetector opening the modal sheet:
GestureDetector(
onTap: () {
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(23.r),
),
),
isScrollControlled: true,
context: context,
builder: (bctx) => StatefulBuilder(builder:
(BuildContext context, StateSetter setModalState) {
return ModalAddFavorite();
}));
},
This is the Widegt that I use as modal sheeet:
class ModalAddFavorite extends StatefulWidget {
const ModalAddFavorite({Key? key}) : super(key: key);
#override
_ModalAddFavoriteState createState() => _ModalAddFavoriteState();
}
class _ModalAddFavoriteState extends State<ModalAddFavorite> {
#override
Widget build(BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter setModalState) {
return Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom * 0.98.h),
//
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 20.h,
),
Container(
width: 80.w,
height: 6.w,
decoration: BoxDecoration(
color: Provider.of<CustomColors>(context, listen: false)
.customColorScheme['Grey 2'],
borderRadius: BorderRadius.circular(6.r),
),
),
SizedBox(
height: 25.h,
),
//
Text(
'ADD A FAVORITE',
style: Provider.of<CustomTextStyle>(context)
.customTextStyle('ModalHeader'),
),
SizedBox(
height: 25.5.h,
),
//
//
InputFieldAddFavorite(),
SizedBox(
height: 40.h,
)
],
),
),
);
});
}
}
Screenshots:
Modal Sheet open / keyboard inactive / no overflow
Modal sheet open / keyboard active / overflow warning in Flutter
Modal shett pulled back manually // overflow visible behind the sheet:
Try to add physics: NeverScrollableScrollPhysics() under your SingleChildScrollView().
Issue solved: Instead of wrapping the modal sheet in a SingleChildScrollView, I needed to wrap the Column that contains the page itself.

How can I send data from bottom sheet to parent widget?

I have list of products in bottom sheet, when I choose any product I want to parent's widget to add it, unfortunately my product adds only after hot reload, or when I create a new route from bottom sheet to parent's widget, how can I solve this problem, any ideas? Here is the part of the bottom sheet code
class IceBottomSheet extends StatefulWidget {
const IceBottomSheet({Key? key}) : super(key: key);
#override
_IceBottomSheetState createState() => _IceBottomSheetState();
}
class _IceBottomSheetState extends State<IceBottomSheet> {
final _model = ProductWidgetsModel();
#override
Widget build(BuildContext context) {
List<Widget> productWidgetList = [];
products.forEach((product) =>
productWidgetList.add(SingleProductWidget(product: product)));
return Provider(
model: _model,
child: Expanded(
child: GridView.count(
crossAxisSpacing: 10,
mainAxisSpacing: 16,
shrinkWrap: true,
crossAxisCount: 2, children: productWidgetList),
),
);
}
}
class SingleProductWidget extends StatefulWidget {
final Product product;
const SingleProductWidget({Key? key, required this.product})
: super(key: key);
#override
State<SingleProductWidget> createState() => _SingleProductWidgetState();
}
class _SingleProductWidgetState extends State<SingleProductWidget> {
#override
Widget build(BuildContext context) {
final model = Provider.of(context)?.model;
return Padding(
padding: const EdgeInsets.all(5.0),
child:
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
width: double.infinity,
height: 100,
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: widget.product.image,
),
shape: BoxShape.circle,
border: Border.all(
color: model?.idSelected == widget.product.id
? Colors.yellow
: Colors.grey,
width: 5.0,
style: BorderStyle.solid,
),
),
child: GestureDetector(
onTap: () {
model?.idSelected = widget.product.id;
// Route route =
// MaterialPageRoute(builder: (context) => BerryPage(context,));
// Navigator.push(context, route);
if(model?.idSelected == 1){
menuRow.removeAt(2);
Navigator.pop(context);
choice.insert(2, Adds(id: 102, name: 'Холодок', img: 'https://autogear.ru/misc/i/gallery/73434/2759438.jpg'));
}
}),
),
),
And here is the part of parent's widget code, it is inside GestureDetector
else if (index == 2){
setState(() {
});
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext builder) {
return Container(
height: 250,
child: Column(
children: [
SizedBox(
height: 10,
),Row(
children: [
Padding(
padding: EdgeInsets.only(left: MediaQuery.of(context).size.width*0.25 + MediaQuery.of(context).size.width *0.12),
child: Text(
'Холодок',
style: TextStyle(
fontFamily: 'Newfont',
fontSize: 22,
),
),
),
SizedBox(width: MediaQuery.of(context).size.width*0.25,),
IconButton(icon: Icon(Icons.close),onPressed: (){Navigator.pop(context);},)
],
),
Divider(),
IceBottomSheet(),
],
));
},
);
So when you open the BottomSheet you have to add await before it, so when you call Navigator.pop(context, data_you_want_to_pass_to_parent) it will wait for some data to be returned.
final data = await openBottomSheet();
inside the bottomSheet when you want to close, just pass the the desired data as so
Navigator.pop(context, data_you_want_to_pass_to_parent);

When I press the card how to set card data into text field in flutter

When I press my card, I want to set my card data into the text field.
my Card Widget and it's another stateful widget. And I need to set this card data into my text field
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
shrinkWrap: true,
children: <Widget>[
Form(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextFormField(
decoration: InputDecoration(
labelText: "CupCake Name",
controller: cupCake,
onChanged: (cupcake) {
cupcakeName = cupcake;
},
),
),
),
]
LoadData(),
),
);
}
This is my Card Widget and it's another stateful widget. And I need to set this card data into my text field
class LoadData extends StatefulWidget {
const LoadData({Key? key}) : super(key: key);
#override
_LoadDataState createState() => _LoadDataState();
}
class _LoadDataState extends State<LoadData> {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
return GestureDetector(
onTap: () => {print("Test")},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Container(
padding: EdgeInsets.all(16),
child: Column(
children: <Widget>[
Text(
data['cupcake_name'],
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
data['description'],
style: TextStyle(
fontSize: 20,
),
),
],
);
}).toList(),
),
}
}
Take a look at flutter simple state management. Basically as you have state that is shared and pertinent to a sub-tree of your widget tree you should encapsulate and lift that state as high as the first widget that needs to interact with it.
1 create the model which will handle the needed state
class CupCakeState extends ChangeNotifier {
CupCake? cupcake;
void selectCupcake(CupCake newCake){
this.cupCake=newCupcake;
notifyListeners();
}
2 Provide the model in the widget tree
ChangeNotifierProvider(
create: (context) => CupCakeState(),
child: const MyApp(),
),
3 Now you can use it either reading the context.
In your card:
class _LoadDataState extends State<LoadData> {
#override
Widget build(BuildContext context) {
return Container(
child: ListView(
return GestureDetector(
onTap: () => {context.read<CupCakeState>().selectCupcake(CupCake(name:data['cupcake_name'],price:data['cupcake_price']))},
...
Or creating a widget that rebuilds everytime you call notifylisteners()
Consumer<CupCakeState>(
builder: (context, cupcpakeState, child) {
cupcake.value=cupcpakeState.price;//assign the price to the controller
return TextFormField(
decoration: InputDecoration(
labelText: "CupCake Name",
controller: cupCake,
onChanged: (cupcake) {
cupcakeName = cupcake;
},
),
},
)

TextField stays behind Keyboard when in Focus at BottomSheet on a real device but works on Emulator

I saw some solutions and implemented them. Look Ok on Emulator but doesn't work on Real Device (See screens). So when I click on a textField, the keyboard moves up as per in-focus Textfield and I am able to scroll, however does not happen in real device. Thanks in advance for your time.
BottomSheet launched at FloatinActionButton
showModalBottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
context: context,
builder: (context) => SingleChildScrollView(
child: Container(
child: PostAd(),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
)),
),
);
The method PostAD shows several pages in Container (depending on Index chosen by user)
return Center(
child: Container(
height: MediaQuery.of(context).size.height / 1.5,
child: Center(
child: Container(
child: Column(
children: [
Center(child: radioButtonCreate()), // Radio Buttons to select Page Index
Expanded(child: kadFormList[formIndex]), //Pages i.e forms that has text field
],
),
),
),
import 'package:flutter/material.dart';
import 'package:keyboard_visibility/keyboard_visibility.dart';
main() => runApp(MaterialApp(
home: Scaffold(
body: MyForm(),
),
));
class MyForm extends StatefulWidget {
MyForm() : super();
#override
State<StatefulWidget> createState() => MyFormState();
}
class MyFormState extends State<MyForm> {
bool isKeyboardVisible = false;
#override
void initState() {
super.initState();
KeyboardVisibilityNotification().addNewListener(
onChange: (bool visible) {
if (!visible) {
setState(() {
FocusScope.of(context).unfocus();
});
}
},
);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(20.0, 20, 20, 0),
child: SingleChildScrollView(
reverse: isKeyboardVisible ? true : false,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 100),
child: Column(
children: <Widget>[
for (int i=0; i<100; i++) TextField()
],
),
),
),
);
}
}

Passing value to previous widget

I have simple form , inside it have CircularAvatar when this is pressed show ModalBottomSheet to choose between take picture from gallery or camera. To make my widget more compact , i separated it to some file.
FormDosenScreen (It's main screen)
DosenImagePicker (It's only CircularAvatar)
ModalBottomSheetPickImage (It's to show ModalBottomSheet)
The problem is , i don't know how to passing value from ModalBottomSheetPickImage to FormDosenScreen. Because value from ModalBottomSheetPickImage i will use to insert operation.
I only success passing from third Widget to second Widget , but when i passing again from second Widget to first widget the value is null, and i think the problem is passing from Second widget to first widget.
How can i passing from third Widget to first Widget ?
First Widget
class FormDosenScreen extends StatefulWidget {
static const routeNamed = '/formdosen-screen';
#override
_FormDosenScreenState createState() => _FormDosenScreenState();
}
class _FormDosenScreenState extends State<FormDosenScreen> {
String selectedFile;
#override
Widget build(BuildContext context) {
final detectKeyboardOpen = MediaQuery.of(context).viewInsets.bottom;
print('trigger');
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Tambah Dosen'),
actions: <Widget>[
PopupMenuButton(
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Tambah Pelajaran'),
value: 'add_pelajaran',
),
],
onSelected: (String value) {
switch (value) {
case 'add_pelajaran':
Navigator.of(context).pushNamed(FormPelajaranScreen.routeNamed);
break;
default:
}
},
)
],
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 20),
DosenImagePicker(onPickedImage: (file) => selectedFile = file),
SizedBox(height: 20),
Card(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormFieldCustom(
onSaved: (value) {},
labelText: 'Nama Dosen',
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
prefixIcon: Icon(Icons.email),
labelText: 'Email Dosen',
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
keyboardType: TextInputType.number,
inputFormatter: [
// InputNumberFormat(),
WhitelistingTextInputFormatter.digitsOnly
],
prefixIcon: Icon(Icons.local_phone),
labelText: 'Telepon Dosen',
),
],
),
),
),
SizedBox(height: kToolbarHeight),
],
),
),
Positioned(
child: Visibility(
visible: detectKeyboardOpen > 0 ? false : true,
child: RaisedButton(
onPressed: () {
print(selectedFile);
},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: colorPallete.primaryColor,
child: Text(
'SIMPAN',
style: TextStyle(fontWeight: FontWeight.bold, fontFamily: AppConfig.headerFont),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
textTheme: ButtonTextTheme.primary,
),
),
bottom: kToolbarHeight / 2,
left: sizes.width(context) / 15,
right: sizes.width(context) / 15,
)
],
),
);
}
}
Second Widget
class DosenImagePicker extends StatefulWidget {
final Function(String file) onPickedImage;
DosenImagePicker({#required this.onPickedImage});
#override
DosenImagePickerState createState() => DosenImagePickerState();
}
class DosenImagePickerState extends State<DosenImagePicker> {
String selectedImage;
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () async {
await showModalBottomSheet(
context: context,
builder: (context) => ModalBottomSheetPickImage(
onPickedImage: (file) {
setState(() {
selectedImage = file;
widget.onPickedImage(selectedImage);
print('Hellooo dosen image picker $selectedImage');
});
},
),
);
},
child: CircleAvatar(
foregroundColor: colorPallete.black,
backgroundImage: selectedImage == null ? null : MemoryImage(base64.decode(selectedImage)),
radius: sizes.width(context) / 6,
backgroundColor: colorPallete.accentColor,
child: selectedImage == null ? Text('Pilih Gambar') : SizedBox(),
),
),
);
}
}
Third Widget
class ModalBottomSheetPickImage extends StatelessWidget {
final Function(String file) onPickedImage;
ModalBottomSheetPickImage({#required this.onPickedImage});
#override
Widget build(BuildContext context) {
return SizedBox(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
children: <Widget>[
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(quality: 80, returnFile: ReturnFile.BASE64);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.green,
child: Icon(Icons.camera_alt),
),
),
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(returnFile: ReturnFile.BASE64, isCamera: false);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.blue,
child: Icon(Icons.photo_library),
),
),
],
),
),
);
}
}
The cleanest and easiest way to do this is through Provider. It is one of the state management solutions you can use to pass values around the app as well as rebuild only the widgets that changed. (Ex: When the value of the Text widget changes). Here is how you can use Provider in your scenario:
This is how your model should look like:
class ImageModel extends ChangeNotifier {
String _base64Image;
get base64Image => _base64Image;
set base64Image(String base64Image) {
_base64Image = base64Image;
notifyListeners();
}
}
Don't forget to add getters and setters so that you can use notifyListeners() if you have any ui that depends on it.
Here is how you can access the values of ImageModel in your UI:
final model=Provider.of<ImageModel>(context,listen:false);
String image=model.base64Image; //get data
model.base64Image=resultBase64; //set your image data after you used ImagePicker
Here is how you can display your data in a Text Widget (Ideally, you should use Selector instead of Consumer so that the widget only rebuilds if the value its listening to changes):
#override
Widget build(BuildContext context) {
//other widgets
Selector<ImageModel, String>(
selector: (_, model) => model.base64Image,
builder: (_, image, __) {
return Text(image);
},
);
}
)
}
You could achieve this easily. If you are using Blocs.