#flutter
Вопрос:
Я новичок в Флаттере. У меня возникли проблемы с моей собственной панелью поиска на перетаскиваемом нижнем листе. Я создал перетаскиваемый нижний лист для поиска информации при нажатии на поле поиска на главном экране. Я добавил панель поиска и представление списка на листе, но панель поиска работала неправильно. Он не мог мгновенно отфильтровать данные, он просто показывал результаты при закрытии клавиатуры. Кто-нибудь, помогите мне, пожалуйста.
class AddPersonalInfo extends StatefulWidget { final String mail, password; const AddPersonalInfo({Key? key, required this.mail, required this.password}) : super(key: key); @override _AddPersonalInfoState createState() =gt; _AddPersonalInfoState(); } class _AddPersonalInfoState extends Statelt;AddPersonalInfogt; { TextEditingController _search = TextEditingController(); Listlt;Citygt; cites = [ City(id: 1, code: "HN", name: "Ha Noi"), City(id: 2, code: "HCM", name: "Ho CHi Minh"), City(id: 3, code: "DN", name: "Da Nang"), City(id: 4, code: "HP", name: "Hai Phong"), City(id: 5, code: "CT", name: "Can Tho"), City(id: 6, code: "DNN", name: "Dong Nai"), City(id: 7, code: "KH", name: "Khanh Hoa"), City(id: 8, code: "PY", name: "Phu Yen"), City(id: 9, code: "NT", name: "Nha Trang"), City(id: 10, code: "VL", name: "Vinh Long"), City(id: 11, code: "HD", name: "Hai Duong"), City(id: 12, code: "BD", name: "Binh Duong") ]; City? selected; Listlt;Citygt; foundCity = []; @override void initState() { super.initState(); setState(() { foundCity = cites; }); } @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; Orientation orientation = MediaQuery.of(context).orientation; return GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, child: Scaffold( backgroundColor: Colors.white, body: SafeArea( child: LayoutBuilder(builder: (context, snapshot) { if (snapshot.maxWidth lt;= screenSize.width amp;amp; orientation == Orientation.portrait) { return SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0, 30 * screenScale(context), 0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Stack( children: [ Container( alignment: Alignment.topCenter, padding: EdgeInsets.only( bottom: 20 * screenScale(context)), child: introText("Create account", context), ), backBtn(context), ], ), mainText( "Let's create an account to grab all latest gadgets and enjoy the best experiences.", context), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [_fnameField(), _lnameField()], ), _phoneField(), GestureDetector( onTap: () =gt; showModalBottomSheet( backgroundColor: Colors.transparent, isScrollControlled: true, context: context, builder: (context) =gt; buildSheet(), ), child: Container( height: 50, margin: EdgeInsets.only(top: 15), padding: EdgeInsets.only(left: 10, right: 7), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(7), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ selected == null ? Text("Select city", style: TextStyle( fontSize: 16, color: Colors.grey.shade700)) : Text("${selected!.name}", style: TextStyle( fontSize: 16, color: Colors.black)), Icon(Ionicons.chevron_down_outline, size: 24) ], ), ), ) ], ), ), ); } else { return SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( padding: EdgeInsets.fromLTRB(30 * screenScale(context), 0, 30 * screenScale(context), 0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [], ), ), ); } }), ), ), ); } Widget buildSheet() { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pop(); }, child: DraggableScrollableSheet( initialChildSize: 0.9, builder: (_, controller) =gt; Container( padding: EdgeInsets.fromLTRB(10, 10, 10, 0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), ), child: Column( children: [ Divider( thickness: 4, color: Colors.grey.shade300, endIndent: 170, indent: 170, ), Padding( padding: EdgeInsets.only(bottom: 15, top: 5), child: Text("Select city", style: TextStyle( fontSize: 18, color: Colors.black, fontWeight: FontWeight.bold)), ), Padding( padding: EdgeInsets.only(bottom: 10), child: TextFormField( controller: _search, keyboardType: TextInputType.name, style: TextStyle(fontSize: 16 * fontScale(context)), decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(7 * screenScale(context))), contentPadding: EdgeInsets.only(top: 10 * screenScale(context)), hintText: 'Search', prefixIcon: Icon(Ionicons.search_outline), ), onChanged: (value) { setState(() { foundCity = cites .where( (city) =gt; city.name.toLowerCase().contains(value)) .toList(); }); }, ), ), Expanded( child: foundCity.length gt; 0 ? ListView.builder( itemCount: foundCity.length, itemBuilder: (context, index) { final city = foundCity[index]; return ListTile( title: Text(city.name), onTap: () { Navigator.of(context).pop(); setState(() { selected = city; }); }, ); }, ) : Padding( padding: EdgeInsets.only(top: 50), child: Text( "No data.", style: TextStyle(fontSize: 16), ), ), ), ], ), ), ), ); } }
Ответ №1:
Хорошо, я проверил ваш код, и, похоже, вы упустили небольшую проблему, когда вы создаете showModalBottomSheet, он больше не является частью вашего виджета с отслеживанием состояния
Я настроил код так, чтобы он работал сейчас:
Widget buildSheet(BuildContext context) { return StatefulBuilder(builder: (BuildContext context, setState) { return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { Navigator.of(context).pop(); }, child: DraggableScrollableSheet( initialChildSize: 0.9, builder: (_, controller) =gt; Container( padding: EdgeInsets.fromLTRB(10, 10, 10, 0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), ), child: Column( children: [ Divider( thickness: 4, color: Colors.grey.shade300, endIndent: 170, indent: 170, ), Padding( padding: EdgeInsets.only(bottom: 15, top: 5), child: Text("Select city", style: TextStyle( fontSize: 18, color: Colors.black, fontWeight: FontWeight.bold)), ), Padding( padding: EdgeInsets.only(bottom: 10), child: TextFormField( controller: _search, keyboardType: TextInputType.name, style: TextStyle(fontSize: 16), decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(7)), contentPadding: EdgeInsets.only(top: 10), hintText: 'Search', prefixIcon: Icon(Icons.access_alarm), ), onChanged: (value) { setState(() { foundCity = cites .where((city) =gt; city.name!.toLowerCase().contains(value)) .toList(); }); }, ), ), Expanded( child: foundCity.isNotEmpty ? ListView.builder( itemCount: foundCity.length, itemBuilder: (context, index) { final city = foundCity[index]; return ListTile( title: Text(city.name!), onTap: () { Navigator.of(context).pop(); setState(() { selected = city; }); }, ); }, ) : Padding( padding: EdgeInsets.only(top: 50), child: Text( "No data.", style: TextStyle(fontSize: 16), ), ), ), ], ), ), ), ); }); }
Комментарии:
1. но есть еще одна проблема, я не могу выбрать город без поиска, как раньше, я просто могу выбирать, когда что-то ищется или открывается клавиатура. Надеюсь, ты это исправишь.
2. Хорошо, я не уверен, что это правильный ответ, но вот что вы можете сделать: добавьте контроллер focusNode для своего текстового поля и вызовите его (_focus.requestFocus ()), прежде чем создавать свой список. Это активирует состояние.
3. Большое вам спасибо @WBG. Я уже исправил это, просто добавьте оператор «this.setState (() {})», чтобы вызвать состояние родительского экрана.