FlutterのAutocompleteをCupertinoスタイルにする 2021.09.06
FlutterのAutocompleteをCupertinoスタイルにする
全体のDartPadはこちら。
The user’s text input is received in a field built with the fieldViewBuilder parameter.
Autocomplete
クラスのfieldViewBuilder
でカスタマイズできそうなので、何を指定すればいいか見てみる。
fieldViewBuilder
はAutocompleteFieldViewBuilder
クラスで、Widget
を返す関数のようだ。
typedef AutocompleteFieldViewBuilder = Widget Function(
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted,
);
See also:
- RawAutocomplete, which is what Autocomplete is built upon, and which contains more detailed examples.
RawAutocomplete
のサンプルを見てみると、TextFormField
にcontroller
、focusNode
あたりを渡して返してあげれば良さそう。
// ...
class AutocompleteBasicExample extends StatelessWidget {
const AutocompleteBasicExample({Key? key}) : super(key: key);
static const List<String> _options = <String>[
'aardvark',
'bobcat',
'chameleon',
];
@override
Widget build(BuildContext context) {
return RawAutocomplete<String>(
// ...
fieldViewBuilder: (BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted) {
return TextFormField(
controller: textEditingController,
focusNode: focusNode,
onFieldSubmitted: (String value) {
onFieldSubmitted();
},
);
},
// ...
);
}
}
Autocomplete
のサンプルでTextFormField
をCupertinoTextField
に置き換える。
// ...
class AutocompleteBasicExample extends StatelessWidget {
const AutocompleteBasicExample({Key? key}) : super(key: key);
static const List<String> _kOptions = <String>[
'aardvark',
'bobcat',
'chameleon',
];
@override
Widget build(BuildContext context) {
return Autocomplete<String>(
+ fieldViewBuilder: (
+ BuildContext context,
+ TextEditingController textEditingController,
+ FocusNode focusNode,
+ VoidCallback onFieldSubmitted,
+ ) {
+ return CupertinoTextField(
+ controller: textEditingController,
+ focusNode: focusNode,
+ );
+ },
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (String selection) {
print('You just selected $selection');
},
);
}
}
これで、TextFormField
がCupertinoTextField
に置き換わり、入力内容に反応させることもできる。
MacのキーボードでSimulatorのTextFormFieldに入力するとStack Overflow
本題から外れるが、Simulatorの“I/O”→“Input”→“Send Keyboard Event to Device”、もしくは“I/O”→“Keyboard”→“Connect Hardware Keyboard”オプションを有効にすると、TextFormFieldへの入力がStack Overflowを吐くようになった。
flutter clean
やXCodeのアップデートを行ってみたが、改善しなかった。
══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following StackOverflowError was thrown during a platform message callback:
Stack Overflow
When the exception was thrown, this was the stack:
#0 Duration.toString (dart:core/duration.dart:259:3)
#1 _StringBase._interpolate (dart:core-patch/string_patch.dart:846:19)
#2 Duration.toString (dart:core/duration.dart:275:25)
#3 _StringBase._interpolate (dart:core-patch/string_patch.dart:846:19)
...
...
#5308 ChannelBuffers.push (dart:ui/channel_buffers.dart:329:17)
#5309 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:544:22)
#5310 _dispatchPlatformMessage (dart:ui/hooks.dart:92:31)
(elided 2 frames from dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: Stack Overflow
一時的な対応として、上記の2つのオプションは無効にしてソフトウェアキーボードを使うようにした。
Autocompleteのオプションにモデルを使用する
Autocomplete
のオプションがテキストであることは実用上はあまりなくて、だいたいオブジェクト的なものになるので、Autocomplete
クラスの2番目のサンプルを参考に、モデルを渡せるようにする。まずモデルの定義。
class User {
const User({
required this.id,
required this.name,
required this.email,
});
final int id;
final String name;
final String email;
@override
String toString() {
return '$name, $email';
}
}
次に、String
クラスだったところをUser
クラスに置き換える。displayStringForOption
を使うと、表示や返り値を制御できる。
class AutocompleteBasicExample extends StatelessWidget {
const AutocompleteBasicExample({Key? key}) : super(key: key);
static const List<User> users = <User>[
User(id: 1, name: 'Alpha', email: 'alpha@example.com'),
User(id: 2, name: 'Bravo', email: 'bravo@example.com'),
User(id: 3, name: 'Charlie', email: 'charlie@example.com'),
];
@override
Widget build(BuildContext context) {
return Autocomplete<User>(
displayStringForOption: (User option) => option.name,
fieldViewBuilder: (
BuildContext context,
TextEditingController textEditingController,
FocusNode focusNode,
VoidCallback onFieldSubmitted,
) {
return CupertinoTextField(
controller: textEditingController,
focusNode: focusNode,
);
},
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<User>.empty();
}
return users.where((User option) {
return option
.toString()
.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (User selection) {
print('You just selected $selection.name');
},
);
}
}
AutocompleteをCupertinoスタイルにすることができた。