I have a problem in my app...
I have an game app that needs to implements players at the beginning of the app. So in the first page called PlayerSelectionPage, you can add players to the game. Then you can choose a game mode. Anyway, when I go to settings and I switch the Language of the game, my keyboard pops out for no reason... I think it's somehow related to my PlayerPageSelection and the fact that it rebuild all with providers to change the language.
Here is an example:
Here is my SettingsPage code :
class SettingsPage extends StatelessWidget {
String _status = '';
ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
double index = Provider.of<SettingsProvider>(context).getDrinkIntensity();
if (index == 0) {_status = AppLocalizations.of(context).translate('settings_page_status_1');}
else if (index == 1) {_status = AppLocalizations.of(context).translate('settings_page_status_2');}
else if (index == 2) {_status = AppLocalizations.of(context).translate('settings_page_status_3');}
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).translate('settings_page_title')),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.assessment),
onPressed: () {
HapticFeedback.mediumImpact();
Navigator.push(context, MaterialPageRoute(builder: (context) => StatsPage(), settings: RouteSettings(name: 'Stats page')));
},
tooltip: AppLocalizations.of(context).translate('settings_page_stat_hint'),
iconSize: 25,
)
],
),
body: ListView(
controller: _controller,
children: [
SizedBox(height: 10,),
Center(child: Text(AppLocalizations.of(context).translate('settings_page_drink_text'), style: Theme.of(context).textTheme.subtitle2)),
Center(child: Text(_status, style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: 10))),
Slider(
value: Provider.of<SettingsProvider>(context).getDrinkIntensity()/2,
onChanged: (val) {
HapticFeedback.mediumImpact();
Provider.of<SettingsProvider>(context, listen: false).setDrinkIntensity(val);
},
divisions: 2,
label: _status,
),
Divider(height: 20),
Text(AppLocalizations.of(context).translate('settings_page_lang_title'), style: Theme.of(context).textTheme.subtitle2, textAlign: TextAlign.center,),
LangSelection(langCode: 'fr',),
LangSelection(langCode: 'en', isBeta: true,),
LangSelection(langCode: 'de', isEnabled: false,),
LangSelection(langCode: 'es', isEnabled: false,),
LangSelection(langCode: 'pl', isEnabled: false,),
LangSelection(langCode: 'it', isEnabled: false,),
Text("Flag icons made by Pixel perfect from www.flaticon.com", style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: 10), textAlign: TextAlign.center,),
Divider(height: 20),
ListTile(
title: Text(AppLocalizations.of(context).translate('settings_page_reinitialize_text'), style: Theme.of(context).textTheme.subtitle2),
trailing: IconButton(
icon: Icon(Icons.restore),
onPressed: () => Provider.of<SettingsProvider>(context, listen: false).resetSharedPreferences(),
),
),
Divider(height: 20),
],
)
);
}
}
And here is my PlayerSelectionPage:
class PlayerSelectionPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
int itemCount = Provider.of<PlayerProvider>(context).getPlayerList.length;
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).translate('player_selection_page_title')),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
HapticFeedback.mediumImpact();
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePage(), settings: RouteSettings(name: 'Home page')));
},
child: Icon(
Icons.chevron_right,
size: 30,
color: Colors.white,
),
),
body: itemCount > 0 ? ListView.builder(
itemCount: itemCount,
itemBuilder: (context, index) {
return Column(
children: [
PlayerDismissible(index),
Divider(
height: 0,
)
],
);
}) : Container(
padding: EdgeInsets.all(20),
alignment: Alignment.topCenter,
child: Text(AppLocalizations.of(context).translate('player_selection_page_empty_text'), textAlign: TextAlign.center, style: Theme.of(context).textTheme.subtitle2)
),
bottomSheet: BottomPlayerBar(),
);
}
}
And the BottomPlayerBar (Where I show the keyboard or not:
class BottomPlayerBar extends StatefulWidget {
@override
_BottomPlayerBarState createState() => _BottomPlayerBarState();
}
class _BottomPlayerBarState extends State<BottomPlayerBar> {
String playerName;
FocusNode myFocusNode;
@override
void initState() {
super.initState();
myFocusNode = FocusNode();
myFocusNode.requestFocus();
}
@override
Widget build(BuildContext context) {
return Container(
height: 80, color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(top: 20, bottom: 25, left: 20, right: 70),
child: TextField(
focusNode: myFocusNode,
textCapitalization: TextCapitalization.words,
onChanged: (val) => playerName = val.trim(),
onSubmitted: (val) {
if (playerName != null && playerName != '') {
Provider.of<PlayerProvider>(context, listen: false).addPlayer(playerName);
HapticFeedback.lightImpact();
myFocusNode.requestFocus();
} else {
myFocusNode.unfocus();
}
},
maxLength: 19,
autocorrect: false,
decoration: new InputDecoration(
counterText: "",
border: new OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: const BorderRadius.all(
const Radius.circular(30.0),
),
),
filled: true,
contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 20),
hintStyle: GoogleFonts.rubik(color: Colors.grey[500], fontWeight: FontWeight.bold),
hintText: AppLocalizations.of(context).translate('player_selection_page_hint'),
fillColor: Colors.white),
)
);
}
@override
void dispose() {
super.dispose();
myFocusNode.dispose();
}
}
Any help is welcome !
Thanks !
Edit : Here is my LangSelection() class :
class LangSelection extends StatelessWidget {
String langCode;
bool isBeta, isEnabled;
LangSelection({@required this.langCode, this.isBeta = false, this.isEnabled = true});
@override
Widget build(BuildContext context) {
return InkWell(
highlightColor: Colors.transparent,
onTap: () {
if (isBeta) {
Provider.of<AppLanguageProvider>(context, listen: false).changeLanguage(Locale(langCode));
Future.delayed(Duration(milliseconds: 100)).then((value) => EasyDialog.showInfoDialog(
context: context,
title: AppLocalizations.of(context).translate('settings_page_beta_dialog_title'),
content: AppLocalizations.of(context).translate('settings_page_beta_dialog_content'),
));
} else if (!isEnabled) {
EasyDialog.showInfoDialog(
context: navigatorKey.currentState.overlay.context,
title: AppLocalizations.of(navigatorKey.currentContext).translate('settings_page_disabled_dialog_title'),
content: AppLocalizations.of(navigatorKey.currentContext).translate('settings_page_disabled_dialog_content'),
);
} else {
Provider.of<AppLanguageProvider>(context, listen: false).changeLanguage(Locale(langCode));
}
},
child: ListTile(
leading: Image.asset('assets/images/flags/$langCode.png'),
title: Text(AppLocalizations.of(context).translate('lang_$langCode'), style: Theme.of(context).textTheme.subtitle2),
trailing: isBeta ? Text("(beta)", style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: 10),) :
!isEnabled ? Text("(disabled)", style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: 10),) : null,
),
);
}
}
I find an answer by my self. As @Crazzygamerr said
The problem is with calling requestFocus() in your bottomAppBar's initState(). Whenever you rebuild your widget it gets called.
So I did this :
@override
void initState() {
super.initState();
myFocusNode = FocusNode();
SchedulerBinding.instance.addPostFrameCallback((_) {
if (ModalRoute.of(context).isCurrent) {
myFocusNode.requestFocus();
}
});
}
In the initState of my BottomPlayerBar. What SchedulerBinding.instance.addPostFrameCallback
does is that it waits the build of the class so I can have access to the context
. Then, with ModalRoute.of(context).isCurrent
, I check if the BottomPlayerBar is displayed for the user, if it is, I give the focus and the keyboard appears, if not, it does nothing... So my keyboard doesn't appear anymore when I switch the language.
Thanks !