Original answer: https://stackoverflow.com/questions/44991968/how-can-i-dismiss-the-on-screen-keyboard
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = new TextEditingController();
FocusNode _focusNode = new FocusNode(); //1 - declare and initialize variable
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.send),
onPressed: () {
_focusNode.unfocus(); //3 - call this method here
},
),
body: new Container(
alignment: FractionalOffset.center,
padding: new EdgeInsets.all(20.0),
child: new TextFormField(
controller: _controller,
focusNode: _focusNode, //2 - assign it to your TextFormField
decoration: new InputDecoration(labelText: 'Example Text'),
),
),
);
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
void main() {
runApp(new MyApp());
}
Using with BLoC
- In BLoC, create FocusNode, and set unfocus at any time you need to hide the keyboard
final FocusNode focusNode = FocusNode();
- In Widget, get focusNode directly via BLoC, or for safety you should create a method called hideKeyboard() on BLoC and use it to avoid using focusNode ref
onTap: () {
bloc.focusNode.unfocus();
}
or with TextField
child: TextField(
onChanged: bloc.searchUser.push,
autofocus: true,
focusNode: bloc.focusNode,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Please enter a search term',
),
),
Completed demo: Create a search screen, user give an input text, the app fetch from github api to get data.
Search widget
import 'package:bflutter_poc/detail/detail_screen.dart';
import 'package:bflutter_poc/model/user_base.dart';
import 'package:bflutter_poc/search/search_bloc.dart';
import 'package:flutter/material.dart';
class SearchScreen extends StatefulWidget {
@override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
final bloc = SearchBloc();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Search Screen')),
body: Container(
child: GestureDetector(
onTap: () {
bloc.focusNode.unfocus();
},
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10, right: 10, top: 10),
child: Row(
children: <Widget>[
Icon(Icons.search),
Expanded(
child: TextField(
onChanged: bloc.searchUser.push,
autofocus: true,
focusNode: bloc.focusNode,
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Please enter a search term',
),
),
),
],
),
),
Container(
margin: EdgeInsets.only(bottom: 10),
child: Divider(
color: Colors.black,
),
),
Container(
child: StreamBuilder(
stream: bloc.loading.stream(),
builder: (context, loading) {
if (loading.hasData && loading.data) {
return Center(
child: CircularProgressIndicator(),
);
}
return Container();
},
),
),
Expanded(
child: StreamBuilder(
stream: bloc.searchUser.stream(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
if (!snapshot.hasData || (snapshot?.data)?.length == 0) {
return Text('No data');
}
List<UserBase> users = snapshot.data;
return ListView.builder(
itemCount: users.length,
itemBuilder: (BuildContext context, int index) {
return FlatButton(
child: Row(
children: <Widget>[
CircleAvatar(
backgroundImage:
NetworkImage(users[index].avatarUrl),
radius: 20.0,
),
Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: Text('${users[index].login}'),
),
],
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetailScreen(userBase: users[index])));
},
);
},
);
},
),
),
],
),
)),
);
}
}
Search Bloc
import 'dart:convert';
import 'package:bflutter/bflutter.dart';
import 'package:bflutter_poc/api.dart';
import 'package:bflutter_poc/model/user_base.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
class SearchBloc {
final loading = BlocDefault<bool>();
final searchUser = Bloc<String, List<UserBase>>();
final FocusNode focusNode = FocusNode();
SearchBloc() {
_initSearchUserLogic();
}
void _initSearchUserLogic() {
searchUser.business = (Observable<String> event) => event
.distinct()
.debounceTime(Duration(milliseconds: 1000))
.flatMap((input) {
//show loading
loading.push(true);
if (input.isEmpty) return Observable.just(null);
//hide keyboard
focusNode.unfocus();
return Observable.fromFuture(Api().searchUsers(input));
}).map((data) {
if (data == null) {
return <UserBase>[];
}
if (data.statusCode == 200) {
final List<UserBase> result = json
.decode(data.body)['items']
.cast<Map<String, dynamic>>()
.map<UserBase>((item) => UserBase.fromJson(item))
.toList();
return result;
} else {
throw (data.body);
}
}).handleError((error) {
loading.push(false);
throw error;
}).doOnData((data) {
loading.push(false);
});
}
void dispose() {
loading.dispose();
searchUser.dispose();
}
}