How to set the focus on a materialbutton in flutter - flutter

I want the focus the focus on the material button so I can press enter or click the button an create an item
final FocusNode _createButtonFocusNode = new FocusNode();
#override
void initState() {
FocusScope.of(context).requestFocus(_createButtonFocusNode);
super.initState();
}
RawKeyboardListener(
focusNode: _createButtonFocusNode,
onKey: (RawKeyEvent event) {
if (event.logicalKey == LogicalKeyboardKey.enter) {
_createItem();
}
},
child:RaisedButton(focusNode: _createButtonFocusNode,
onPressed: () {
_createItem();
},
child: Text("Create"))))
Assume also a cancel material button exists with a _cancelItem event that should be able to accept an enter key on focus

You can copy paste run full code below
You can use _node.requestFocus() to request focus and list keyboard event with FocusAttachment and attach
In demo code, when receive Enter will change button color, see working demo below
code snippet
_node.requestFocus();
...
FocusAttachment _nodeAttachment;
_nodeAttachment = _node.attach(context, onKey: _handleKeyPress);
...
bool _handleKeyPress(FocusNode node, RawKeyEvent event) {
if (event is RawKeyDownEvent) {
print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}');
if (event.logicalKey == LogicalKeyboardKey.enter) {
print('clicked enter');
setState(() {
_color = Colors.deepPurple;
});
return true;
}
}
return false;
}
working demo
full code
// Flutter code sample for FocusNode
// This example shows how a FocusNode should be managed if not using the
// [Focus] or [FocusScope] widgets. See the [Focus] widget for a similar
// example using [Focus] and [FocusScope] widgets.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: MyStatelessWidget(),
),
);
}
}
class CustomButton extends StatefulWidget {
FocusNode focusNode;
CustomButton({Key key, this.focusNode}) : super(key: key);
#override
_CustomButtonState createState() => _CustomButtonState();
}
class _CustomButtonState extends State<CustomButton> {
bool _focused = false;
FocusAttachment _nodeAttachment;
Color _color = Colors.white;
#override
void initState() {
super.initState();
//widget.focusNode = FocusNode(debugLabel: 'Button');
widget.focusNode.addListener(_handleFocusChange);
_nodeAttachment = widget.focusNode.attach(context, onKey: _handleKeyPress);
}
void _handleFocusChange() {
print(widget.focusNode.hasFocus);
if (widget.focusNode.hasFocus != _focused) {
setState(() {
_focused = widget.focusNode.hasFocus;
_color = Colors.white;
});
}
}
bool _handleKeyPress(FocusNode node, RawKeyEvent event) {
if (event is RawKeyDownEvent) {
print('Focus node ${node.debugLabel} got key event: ${event.logicalKey}');
if (event.logicalKey == LogicalKeyboardKey.enter) {
print('clicked enter');
setState(() {
_color = Colors.deepPurple;
});
return true;
}
}
return false;
}
#override
void dispose() {
widget.focusNode.removeListener(_handleFocusChange);
// The attachment will automatically be detached in dispose().
widget.focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
_nodeAttachment.reparent();
return Center(
child: RaisedButton(
focusNode: widget.focusNode,
color: _focused ? _color : Colors.white,
child: Text(_focused ? "focused" : 'Not focus'),
onPressed: () {
print("create item");
},
),
);
}
}
class MyStatelessWidget extends StatefulWidget {
MyStatelessWidget({Key key}) : super(key: key);
#override
_MyStatelessWidgetState createState() => _MyStatelessWidgetState();
}
class _MyStatelessWidgetState extends State<MyStatelessWidget> {
FocusNode _node1 = FocusNode();
FocusNode _node2 = FocusNode();
#override
Widget build(BuildContext context) {
final TextTheme textTheme = Theme.of(context).textTheme;
return DefaultTextStyle(
style: textTheme.headline4,
child: Column(
children: [
CustomButton(
focusNode: _node1,
),
CustomButton(
focusNode: _node2,
),
RaisedButton(
onPressed: () {
_node1.requestFocus();
setState(() {});
},
child: Text("request focus button 1")),
RaisedButton(
onPressed: () {
_node2.requestFocus();
setState(() {});
},
child: Text("request focus button 2")),
],
),
);
}
}

If all you want is for the button to be focused by default, you can do that by just specifying autofocus:true on the button, and you don't even need to create a FocusNode:
class MyCustomWidget extends StatelessWidget {
const MyCustomWidget({Key? key}) : super(key: key);
void _createItem() {
print('Item created');
}
#override
Widget build(BuildContext context) {
return TextButton(
autofocus: true,
child: const Text('CREATE'),
onPressed: _createItem,
);
}
}
This will automatically focus the widget when first built, as long as something else doesn't have the focus already.
If you need to set the focus from another control, you can do that with a focus node, but you don't need to use a FocusAttachment (you rarely, if ever, need to use one of those), you can just pass it to the button and call requestFocus() on it.
class MyCustomWidget extends StatefulWidget {
const MyCustomWidget({Key? key}) : super(key: key);
#override
State<MyCustomWidget> createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget> {
late FocusNode _createButtonFocusNode;
#override
void initState() {
super.initState();
_createButtonFocusNode = FocusNode();
}
#override
void dispose() {
_createButtonFocusNode.dispose();
super.dispose();
}
void _createItem() {
print('Item created');
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
child: const Text('FOCUS OTHER BUTTON'),
onPressed: () => _createButtonFocusNode.requestFocus(),
),
TextButton(
focusNode: _createButtonFocusNode,
child: const Text('CREATE'),
onPressed: _createItem,
),
],
),
);
}
}
(When you do create a FocusNode, be sure to dispose of it properly.)

Related

how to disable button in Flutter 3.3.4

in Flutter 3.3.4 , I want control the state of the button by passing an object with its properties
. I tried some solutions in stackoverflow (e.g How do I disable a Button in Flutter? ),but failed。
I print the flag of the object , it looks right.
here is my code
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
SwitchWidget wifiSwitch = SwitchWidget();
// SwitchWidget timeSwitch = SwitchWidget();
// SwitchWidget locationSwitch = SwitchWidget();
return MaterialApp(
title: 'Startup N1ame Generator',
home: Scaffold(
appBar: AppBar(
title: const Text('Startup Name Generator'),
),
body: Center(
child: Row(
children: [
Column(children: [wifiSwitch]),
Column(children: [ButtonWidget(wifiSwitch)])
],
),
),
),
);
}
}
class SwitchWidget extends StatefulWidget {
bool flag = true;
SwitchWidget({Key? key}) : super(key: key);
#override
State<SwitchWidget> createState() => _SwitchWidgetState(this);
}
class _SwitchWidgetState extends State<SwitchWidget> {
SwitchWidget switchWidget;
_SwitchWidgetState(this.switchWidget);
#override
Widget build(BuildContext context) {
return Container(
child: Switch(
value: switchWidget.flag,
onChanged: (newValue) => {
setState(() {
switchWidget.flag = newValue;
print("-----------${switchWidget.flag}");
})
},
),
);
}
}
class ButtonWidget extends StatefulWidget {
late SwitchWidget _switchWidget;
SwitchWidget get switchWidget => _switchWidget;
set switchWidget(SwitchWidget switchWidget) => {
print('The ButtonWidget is $switchWidget.'),
_switchWidget = switchWidget
};
ButtonWidget(switchWidget, {Key? key}) : super(key: key) {
this.switchWidget = switchWidget;
}
#override
State<ButtonWidget> createState() => _ButtonWidgetState(switchWidget);
}
class _ButtonWidgetState extends State<ButtonWidget> {
SwitchWidget switchWidget;
_ButtonWidgetState(this.switchWidget);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(50, 1, 1, 1),
child: ElevatedButton(
// color: Colors.blue,
// disabledColor: Colors.grey,
// textColor: Colors.black,
child: Text("123"),
// onPressed: () {},
onPressed: this.switchWidget.flag ? _incrementCounter : null,
style: ButtonStyle(
foregroundColor: MaterialStateProperty.resolveWith(
(states) {
if (states.contains(MaterialState.disabled)) {
return Colors.grey;
} else {
return Colors.white;
}
},
),
)),
);
{}
}
void _incrementCounter() {
print("object******** ${this.switchWidget.flag}");
}
}
Why do you pass a reference of SwitchWidget to _SwitchWidgetState? You should move the property bool flag = true; to _SwitchWidgetState and then change it directly in setState(() => flag = newValue);.
Also, your ButtonWidget is not rebuilt on change in SwitchWidget. You'll have to use some sort of state management in order to disable the button on a state change of your switch widget.
For example using callbacks:
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
#override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
bool _isDisabled = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
SwitchWidget(initialValue: true, onChanged: (val) => setState(() => _isDisabled = val)),
ButtonWidget(isDisabled: _isDisabled),
],
);
}
}
class SwitchWidget extends StatefulWidget {
final bool initialValue;
final void Function(bool) onChanged;
const SwitchWidget({super.key, required this.onChanged, required this.initialValue});
#override
State<SwitchWidget> createState() => _SwitchWidgetState();
}
class _SwitchWidgetState extends State<SwitchWidget> {
late bool _value;
#override
void initState() {
super.initState();
_value = widget.initialValue;
}
#override
Widget build(BuildContext context) {
return Switch(
value: _value,
onChanged: (val) {
setState(() => _value = val);
widget.onChanged(val);
},
);
}
}
class ButtonWidget extends StatelessWidget {
final bool isDisabled;
const ButtonWidget({super.key, required this.isDisabled});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: isDisabled
? null
: () {
//Some logic
},
child: Text("Press me!"),
);
}
}
You can pass null where you place your function, or even some of Flutter's Widgets already have the enabled property. But setState and change the function to null and you should get what you want.

InputTextField Focus Node working in reverse manner

I wrote code for custom keyboard in Scaffold and set keyboardType: TextInputType.none to close the default keyboard. The code is working but opposite to expected behavior. When I am focusing on TextField Keyboard disappears and when I am out of focus Keyboard appears. Why is this happening. What is the bug in the code?
class MyKeyBoardApp extends StatelessWidget {
const MyKeyBoardApp({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: KeyboardDemo(),
);
}
}
class KeyboardDemo extends StatefulWidget {
const KeyboardDemo({Key? key}) : super(key: key);
#override
_KeyboardDemoState createState() => _KeyboardDemoState();
}
class _KeyboardDemoState extends State<KeyboardDemo> {
final TextEditingController _controller = TextEditingController();
late FocusNode _focus;
#override
void initState() {
super.initState();
_focus.addListener(_onFocusChange);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
_focus.removeListener(_onFocusChange);
_focus.dispose();
}
void _onFocusChange() {
debugPrint("Focus: ${_focus.hasFocus.toString()}");
}
void buildBottomSheet() {
showModalBottomSheet(
barrierColor: Colors.transparent,
builder: (BuildContext context) {
return CustomKeyboard(
onTextInput: (myText) {
_insertText(myText);
},
onBackspace: () {
_backspace();
},
);
},
context: context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Column(
children: [
const SizedBox(height: 50),
Container(
color: Colors.amber,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
keyboardType: TextInputType.none,
focusNode: _focus,
onTap: () {
debugPrint(
"Focus in bottom Sheet: ${_focus.hasFocus.toString()}");
buildBottomSheet();
},
),
),
),
],
),
);
}

How to set focus to TextField in ListView

I have a ListView that has a TextField widget in its children. Listview's items can be changed dynamically. When I press the "Add row" button, a new row should be added and the textfield belongs to newly added row should be focused (keyboard should be shown.) How can I achieve this?
Here is my sample code:
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
MainPage({Key key}) : super(key: key);
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
List<String> list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('NoteList App'),
),
body: Column(children: <Widget>[
Expanded(child: _buildList(context)),
FlatButton(
onPressed: () {
setState(() {
list.add('new');
});
},
child: Text('Add row'))
]));
}
Widget _buildList(BuildContext context) {
return ListView.builder(
itemCount: list.length,
padding: const EdgeInsets.only(top: 1.0),
itemBuilder: (context, index) {
return ListItem(textContent: list[index]);
},
);
}
}
class ListItem extends StatelessWidget {
var _txt = TextEditingController();
final String textContent;
ListItem({Key key, this.textContent}) : super(key: key) {
_txt.text = textContent ?? '';
}
#override
Widget build(BuildContext context) {
return TextField(
controller: _txt,
textInputAction: TextInputAction.go,
);
}
}
You can copy paste run full code below
You can define an Item class and put FocusNode in it
then use FocusScope.of(context).requestFocus
code snippet
class Item {
String textContent;
FocusNode myFocusNode;
TextEditingController myController;
Item(this.textContent, this.myFocusNode, this.myController);
}
List<Item> list = [
Item('one', FocusNode(), TextEditingController()),
Item('two', FocusNode(), TextEditingController()),
Item('three', FocusNode(), TextEditingController()),
Item('four', FocusNode(), TextEditingController())
];
onPressed: () {
setState(() {
list.add(Item('new', FocusNode(), TextEditingController()));
});
WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context)
.requestFocus(list[list.length - 1].myFocusNode);
});
},
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Item {
String textContent;
FocusNode myFocusNode;
TextEditingController myController;
Item(this.textContent, this.myFocusNode, this.myController);
}
class MainPage extends StatefulWidget {
MainPage({Key key}) : super(key: key);
_MainPageState createState() => _MainPageState();
}
List<Item> list = [
Item('one', FocusNode(), TextEditingController()),
Item('two', FocusNode(), TextEditingController()),
Item('three', FocusNode(), TextEditingController()),
Item('four', FocusNode(), TextEditingController())
];
class _MainPageState extends State<MainPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('NoteList App'),
),
body: Column(children: <Widget>[
Expanded(child: _buildList(context)),
FlatButton(
onPressed: () {
setState(() {
list.add(Item('new', FocusNode(), TextEditingController()));
});
WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context)
.requestFocus(list[list.length - 1].myFocusNode);
});
},
child: Text('Add row'))
]));
}
Widget _buildList(BuildContext context) {
return ListView.builder(
itemCount: list.length,
padding: const EdgeInsets.only(top: 1.0),
itemBuilder: (context, index) {
return ListItem(index: index);
},
);
}
#override
void dispose() {
list.forEach((element) {
element.myFocusNode.dispose();
element.myController.dispose();
});
super.dispose();
}
}
class ListItem extends StatefulWidget {
final int index;
ListItem({Key key, this.index}) : super(key: key);
#override
_ListItemState createState() => _ListItemState();
}
class _ListItemState extends State<ListItem> {
#override
void initState() {
super.initState();
list[widget.index].myController.text = list[widget.index].textContent;
}
#override
Widget build(BuildContext context) {
return TextField(
focusNode: list[widget.index].myFocusNode,
controller: list[widget.index].myController,
textInputAction: TextInputAction.go,
);
}
}
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: MainPage(),
);
}
}

Flutter scaffold updated entire page when update on appbar

So I have a scaffold with body is a list view. And I have an appbar that manage its stage. Here my appbar code :
import 'package:flutter/material.dart';
class HgAppBar extends StatefulWidget implements PreferredSizeWidget {
final String title;
final List<Widget> actions;
HgAppBar({this.title, this.actions, Key key}) : super(key: key);
#override
HgAppBarState createState() => HgAppBarState();
#override
Size get preferredSize => new Size.fromHeight(kToolbarHeight);
}
class HgAppBarState extends State<HgAppBar> {
bool _searchOpenned = false;
void openSeach() {
setState(() {
_searchOpenned = true;
});
}
void closeSearch() {
setState(() {
_searchOpenned = true;
});
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return AppBar(
title: _searchOpenned
? TextField(
decoration: InputDecoration(
filled: true,
border: null,
fillColor: Colors.white,
),
autofocus: true,
)
: Text(widget.title ?? 'No title'),
actions: _searchOpenned
? [
IconButton(
icon: Icon(Icons.close),
onPressed: () {
setState(() {
_searchOpenned = false;
});
},
)
]
: widget.actions,
);
}
}
And here my page code:
class PageSales extends StatefulWidget {
final Store<AppState> store;
final String title;
final bool usePop;
PageSales(this.store, {this.title, this.usePop = false});
#override
State<StatefulWidget> createState() => _PageSales();
}
class _PageSales extends State<PageSales> {
final appBarKey = GlobalKey<HgAppBarState>();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HgAppBar(
key: appBarKey,
title: Localizations.of(context, AppLoc).text('sales_plus'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
appBarKey.currentState.openSeach();
},
)
],
),
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: FireStoreListView(
snapshot: HgFirestore.instance.productCollection.snapshots(),
itemBuilder: (context, doc) {
return WidgetProductItem(
widget.store, ProductModel.fromDocument(doc));
},
),
),
]),
),
);
}
}
so the problem is when I call the openSearch, my entire scaffold get refresh (I know it because my ListView is flashing). How do I can update my appbar without refreshing entire scaffold?
I tried your code and it seems to be fine. The screen doesn't rebuild, I'm using Flutter 2.2. I suggest adding debugPrint to make sure that the screen does get rebuild, ListView flashing isn't a definite indicator that the entire screen gets rebuild.

Change widget properties by it's controller in flutter

let's say I have a TextField like this
TextEditingController textController = TextEditingController()
...
TextField(
controller: textController
)
and assume I have a library which takes the controller of a TextField and prints some text inside it.
is it possible to change the properties of TextField or any other widget by it's controller ?
for example: disable the TextField
if No how can I do that inside my library, not inside a Stateful widget
I created this sample , you can take a look :
class SampleTextField extends StatefulWidget {
#override
SampleTextFieldState createState() {
return new SampleTextFieldState();
}
}
class SampleTextFieldState extends State<SampleTextField> {
MyCustomController customController =
MyCustomController(controller: TextEditingController());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
MyCustomTextField(
customController: customController,
),
OutlineButton(
onPressed: () {
setState(() {
customController.controller.text = "new value";
});
},
child: Text("set text"),
),
OutlineButton(
onPressed: () {
setState(() {
customController.enable = false;
});
},
child: Text("disable textfield"),
),
OutlineButton(
onPressed: () {
setState(() {
customController.enable = true;
});
},
child: Text("enable textfield"),
)
],
),
),
);
}
}
class MyCustomTextField extends StatelessWidget {
final MyCustomController customController;
const MyCustomTextField({Key key, this.customController}) : super(key: key);
#override
Widget build(BuildContext context) {
return TextFormField(
controller: customController.controller,
enabled: customController.enable,
);
}
}
class MyCustomController {
final TextEditingController controller;
bool enable;
MyCustomController({#required this.controller, this.enable = true});
}