Widgets tree rebuild using FocusScope.of(context).unfocus() - flutter

I have this example:
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(30),
child: GestureDetector(
onTap: () {
print('Hide keyboard!!!');
FocusScope.of(context).unfocus();
},
child: Column(
children: [
Text(DateTime.now().toIso8601String()),
TextFormField()
],
),
),
),
),
);
}
When the keyboard appears or is hidden it causes the widget to rebuild. Why does this happen?

Actually, I couldn't find the reason behind the rebuild after using
FocusScope.of(context).unfocus();
But This one will help you to stop rebuild the widget.
FocusManager.instance.primaryFocus.unfocus();
It's working on my application.

Related

Register tap on empty space around widget

Working on a flutter web project. I have a row which has 3 widgets:
From left to right:
Sidebar
Sidebar content
body
Widget _buildBody() {
final screenwidth = MediaQuery.of(context).size.width;
editpanel = screenwidth * 0.3;
final editor = ViewProvider.of(context).isEditPanelOpen
? (screenwidth - sidebar - editpanel)
: (screenwidth - sidebar);
final ViewProvider viewProvider = Provider.of<ViewProvider>(context);
return Row(
Sidebar()
_loadSidebarContent(bloc.editPanelIndex),
_sidebarHandler(viewProvider),
Center(
child: SizedBox(
width: editor * 0.8,
child: Center(
child: MyWidget(),
),
),
),
],
);
}
I need to register tap if user taps on anything except the Appbar, Sidebar, Sidebarcontent, on MyWidget.
So I wrapped the entire scaffold with gesture detector and tried using IgnorePointer for the specific widgets.
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
.. call some specific function
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: Size(
MediaQuery.of(context).size.width,
height + 80,
),
child: IgnorePointer(
child: Appbar(),
ignoring: true,
),
),
body: _buildBody(),
),
);
}
Issue is: MyWidget is getting ignored all the time. I don't want to fire the specificFunc() when user taps on any of the: Appbar, Sidebar, Sidebarcontent, or MyWidget.
Basically if user taps the white space around MyWidget specificFunction will be called
Wrap the whole Scaffold widget with GestureDector is not a good idea.
Instead wrap the container (white space around your button) with the detector and supply the button as a child.
In the following sample, the amber area is your white one. Tapping the amber area, and the button produces a separate log.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () {
if (kDebugMode) {
print('Amber area tapped!');
}
},
child: Container(
color: Colors.amber,
width: 400,
height: 400,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
style: ButtonStyle(
foregroundColor:
MaterialStateProperty.all<Color>(Colors.blue),
backgroundColor: MaterialStateProperty.all<Color>(
Colors.white)),
onPressed: () {
if (kDebugMode) {
print('Button clicked.');
}
},
child: const Text('A Button'),
),
],
)),
),
)
],
),
),
);
}
}
You could use a stack (https://api.flutter.dev/flutter/widgets/Stack-class.html) and wrap the widget at the very bottom of the stack with a gesture detector.
To position the other widgets correctly, you could use the Positioned widget.
Instead of ignorePointer you should be using AbsorbPointer which will absorb the pointer and not pass it to the content below it

Flutter Widgets are moving if Keyboard appears even though resize is set to false

I have a weird behavior inside my app... I am using a PageView and on the 2nd page I am focusing a TextField on the pages init. That is working fine. However on the first page my Widgets are moving a little bit even though I set in the wrapping Scaffold: resizeToAvoidBottomInset = false.
Here is a Screenvideo for a better understanding.
This is my Wrapping View:
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: AppColors.secondary,
body: SafeArea(
child: Column(
children: [
_buildDismissButton(),
Expanded(
child: PageView(
controller: _pageController,
onPageChanged: (index) {
if (index == 0) {
FocusScope.of(context).unfocus();
}
},
children: [
AddPhotoPage(onNextPage: () {
_pageController.nextPage(
duration: Duration(milliseconds: 350),
curve: Curves.easeInOut);
}),
AddTitlePage(),
],
),
),
],
),
),
);
}
And the problem is on the AddPhotoPage:
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: sidePadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Foto wählen',
style: AppTextStyles.montserratH2SemiBold,
),
Spacer(flex: 1),
_buildEmptyImageStateWidgets(),
Spacer(
flex: 3,
),
RoundedCornersTextButton(
title: 'Weiter',
isLoading: isUploadingImage,
onTap: () {
widget.onNextPage();
},
),
SizedBox(
height: 50.scaled,
),
],
),
);
}
I feel like it has got something to do with the Spacer(). Because if I removed them and just place SizedBoxes with a specific height, the behavior does not occur.
What am I missing here? Let me know if you need any more info!

Flutter: How to have one tap handler for two widgets?

I've got an IconButton and Text as the children of a Row widget. Currently when user taps on the calendar icon, its onPressed is handled and a calendar is shown to pick a date. However I want to extend the tapping area and allow the calendar to open even when user taps on the Text widget. What's the best way to achieve this purpose?
Please note that there are more children in this Row. I only want to handle the tap on these two children.
InkWell might do the trick for you.
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
),
);
}
}
In response to the edit:
import 'package:flutter/material.dart';
class DoubleTapPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: [
Text('Outside the well'),
InkWell(
child: Row(
children: [
Icon(Icons.calendar_today),
Text('2021 Jan 23')
],
),
onTap: () => print('Calendar or Date tapped'),
),
],
),
),
);
}
}
Wrap the whole Row in GestureDetector as following
GestureDetector(
onTap: () {
// what your want here
}
child: Row(
// ...
),
),

Flutter: Button should output an widget but doesn't

This is my function:
Widget RandomSkill(Widget SkillJonglieren) {
return Widget SkillJonglieren;
This is the stateless Widget "SkillJonglierten"
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Column(
children: <Widget>[
Text('Jonglieren'),
Image.asset('images/Jonglieren.jpg')
],
),
),
);
}
}
This is the button
body: Align(alignment: Alignment.bottomCenter,
child: RaisedButton(
onPressed: RandomSkill,
child: Text('Zufälliger Skill')
),
));
I just want, that when i press the button that this widget is shown
You need a StatefulWidget. In its State class, you need to remember whether the SkillJonglierten is shown or not. If it's shown, you need to show it in your build() function. Otherwise, you should not show it in your build() function. So, this becomes: https://codepen.io/gazialankus/pen/rNxgBgx
class _MyHomePageState extends State<MyHomePage> {
bool isShown;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('Show'),
onPressed: () {
setState(() {
isShown = true;
});
}
),
if (isShown == true) SkillJonglieren()
],
),
),
);
}
}

Determine Scroll Widget height

I would like to determine if a 'scrollable' widget indeed needs to scroll. I would ultimately like to show something like 'Scroll for more'. How do I achieve this?
I could use a combination of LayoutBuilder and a ScrollController. However, ScrollController gives me maxScrollExtent and minScrollExtent only after any event - like say for example user trying to scroll
I would like to know during 'build' so that I can determine to show 'Scroll for more' or not depending on the screen dimensions
class _MyHomePageState extends State<MyHomePage> {
int value = 0;
String text = 'You have pushed the button 0 times\n';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
child: Text(text),
),
),
Expanded(
flex: 1,
child: Text('Scroll for more')),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
value += 1;
text = text + 'You have pushed the button $value times \n';
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I would like to dynamically display
Text('Scroll for more'),
only if the SingleChildScrollView needs to be scrolled to view the entire content. Please note, I am just giving the above code as example.
In my real case, I have a StreamBuilder inside a SingleChildScrollView and I cannot determine how much of data is going to be flowing in from the stream. I also do not have any button or tap or any gesture to access the controller and setState
Thanks in advance for any help
class _MyHomePageState extends State<MyHomePage> {
bool showMore = false;
final scrollController = ScrollController();
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
showMore = scrollController.position.maxScrollExtent > 0;
});
});
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
controller: scrollController,
child: SizedBox(height: 650, child: Text('blah')),
),
),
if (showMore) Expanded(flex: 1, child: Text('Scroll for more')),
],
),
),
);
}
}