简体   繁体   English

如何在下拉列表底部制作一个按钮

[英]how to make a button at the bottom of the drop down list

to create the popup list i used dropdown_search创建我使用dropdown_search的弹出列表

design example as I would like to see it我想看到的设计示例

when I start searching through the elements of the list, a button for adding should pop up当我开始搜索列表的元素时,应该会弹出一个添加按钮

or the button should be immediately when we start searching for the element或者按钮应该在我们开始搜索元素时立即出现

drop-down list下拉列表

下拉列表

button bottom按钮底部

按钮底部

I chose all possible methods in the library, but still I can't use it with the addition of a button我在库中选择了所有可能的方法,但仍然无法通过添加按钮来使用它

all code https://github.com/dimapichuev2000/test_dropdownbutton所有代码https://github.com/dimapichuev2000/test_dropdownbutton

main.dart main.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:dropdown_search/dropdown_search.dart';
import 'package:test_dropdownbutton/gym_model.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData().copyWith(
        primaryColor: Colors.amber,
        colorScheme: ThemeData().colorScheme.copyWith(
              primary: Colors.orange,
            ),
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 
  final _multiKey = GlobalKey<DropdownSearchState<String>>();

  Widget _customDropDownExample(BuildContext context, GymModel? item) {
    if (item == null) {
      return const Text(
        "Выберите фитнесс центр",
        style:
            TextStyle(fontSize: 15, color: Color.fromRGBO(33, 33, 33, 0.6)),
      );
    }
    return Container(
      
      child: (item.avatar == "")
          ? ListTile(
              contentPadding: EdgeInsets.all(0),
              leading: const CircleAvatar(
                radius: 25,
                backgroundColor: Colors.white,
                backgroundImage: NetworkImage(
                    'https://img.icons8.com/ios/50/000000/gum-.png'),
              ),
              title: Text(item.gym),
              subtitle: Text(
                item.location,
              ),
            )
          : ListTile(
              contentPadding: const EdgeInsets.all(0),
              leading: CircleAvatar(
                radius: 25,
                backgroundImage: NetworkImage(item.avatar ?? ''),
              ),
              title: Text(item.gym),
              subtitle: Text(
                item.location,
              ),
            ),
    );
  }

  Widget _exitPopup(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(20),
          topRight: Radius.circular(20),
        ),
      ),
      padding: const EdgeInsets.only(top: 2),
      child: Row(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Container(
            margin: EdgeInsets.all(8),
            height: 30,
            width: 30,
          ),
          const Text(
            "Фитнесс центр",
            style: TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
          ),
          IconButton(
            icon: Image.asset(
              'assets/button_X.png',
              width: 25,
              height: 25,
              fit: BoxFit.fill,
            ),
            iconSize: 25,
            onPressed: () {
              Navigator.of(context).pop();
            },
          )
        ],
      ),
    );
  }

  Widget _customPopupItemBuilderExample(
      BuildContext context, GymModel? item, bool isSelected) {
    return Container(
      decoration: !isSelected
          ? null
          : BoxDecoration(
              borderRadius: BorderRadius.circular(5),
              color: Colors.white,
            ),
      child: ListTile(
          selected: isSelected,
          title: Text(item?.gym ?? ''),
          subtitle: Text(item?.location ?? ''),
          leading: (item!.avatar == "")
              ? const CircleAvatar(
                  radius: 25,
                  backgroundColor: Colors.white,
                  backgroundImage: NetworkImage(
                      'https://img.icons8.com/ios/100/000000/gum-.png'),
                )
              : CircleAvatar(
                  radius: 25,
                  backgroundImage: NetworkImage(item.avatar ?? ''),
                )),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Key value Pair - DropdownButton'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            Container(
              margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),

              child: SingleChildScrollView(
                child: DropdownSearch<GymModel>(
                  mode: Mode.BOTTOM_SHEET,
                  showSearchBox: true,
                  compareFn: (item, selectedItem) => item?.id == selectedItem?.id,
                  scrollbarProps:ScrollbarProps(
                    radius: Radius.circular(20),
                    thickness: 4,

              
                  ),
                  onFind: (String? filter) => getData(filter),
                  dropdownSearchDecoration: const InputDecoration(
                    filled: true,
                    fillColor: Color(0xFFE3E3E4),
                    hintText: "Выберите фитнесс центр",
              
                    border: OutlineInputBorder(
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(10),
                        topRight: Radius.circular(10),
                        bottomLeft: Radius.circular(10),
                        bottomRight: Radius.circular(10),
                      ),
                      borderSide: BorderSide(
                        width: 0,
                        style: BorderStyle.none,
                      ),
                    ),
                    contentPadding: EdgeInsets.fromLTRB(12, 3, 0, 0),
                  ),
                  popupSafeArea:
                      const PopupSafeAreaProps(top: true, bottom: true),
                  // labelText: "Выберите фитнесс центр ",
                  onChanged: (data) {
                    print(data);
                  },
                  maxHeight: 850,
                  dropdownBuilder: _customDropDownExample,
                  popupItemBuilder: _customPopupItemBuilderExample,
                  dropDownButton: Image.asset('assets/dropButton.png',
                      color: Color(0xFF79818A)),
                  popupTitle: _exitPopup(context),
                  popupShape: const RoundedRectangleBorder(
                    borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(10),
                      topRight: Radius.circular(10),
                    ),
                  ),
                  searchFieldProps: TextFieldProps(
                    decoration: const InputDecoration(
                        prefixIcon: Icon(
                          Icons.search,
                        ),
                        filled: true,
                        fillColor: Color(0xFFE3E3E4),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(10),
                            topRight: Radius.circular(10),
                            bottomLeft: Radius.circular(10),
                            bottomRight: Radius.circular(10),
                          ),
                          borderSide: BorderSide(
                            width: 0,
                            style: BorderStyle.none,
                          ),
                        ),
                        contentPadding: EdgeInsets.fromLTRB(12, 12, 8,0),
                        // labelText: "Поиск",
                        hintText: "Поиск",
                        hintStyle: TextStyle(fontSize: 17)),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<List<GymModel>> getData(filter) async {
    var response = await Dio().get(
      "https://my-json-server.typicode.com/dimapichuev2000/JSON_TEST/posts",
      queryParameters: {"filter": filter},
    );

    final data = response.data;
    if (data != null) {
      return GymModel.fromJsonList(data);
    }

    return [];
  }
}

gym_model.dart gym_model.dart

class GymModel {
  final String id;
  final String location;
  final String gym;
  final String? avatar;

  GymModel(
      {required this.id,
      required this.location,
      required this.gym,
      this.avatar});

  factory GymModel.fromJson(Map<String, dynamic> json) {
    return GymModel(
      id: json["id"],
      location: json["location"],
      gym: json["gym"],
      avatar: json["avatar"],
    );
  }

  static List<GymModel> fromJsonList(List list) {
    return list.map((item) => GymModel.fromJson(item)).toList();
  }


  @override
  String toString() => gym;
}

在此处输入图像描述

pubspec.yaml pubspec.yaml

name: fitsupp_coach
description: A new Flutter project.

# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.12.0 <3.0.0"

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter_localizations:
    sdk: flutter 
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  pinput: ^1.2.2
  intl: ^0.17.0
  quiver: ^3.0.1+1
  firebase_auth: ^3.3.5
  firebase_core: ^1.11.0
  dropdown_search: ^2.0.1
  dio: ^4.0.4
  provider: ^6.0.2
  percent_indicator: ^4.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^1.0.0
  

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
   - assets/images/
   - assets/images/DropDown/

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  fonts:
    - family: SF Pro Display
      fonts:
        - asset: fontsassets/SFProDisplay/SFProDisplay-Light.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Medium.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Black.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Bold.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Heavy.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Regular.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Semibold.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Thin.ttf
        - asset: fontsassets/SFProDisplay/SFProDisplay-Ultralight.ttf
          style: normal
    - family: SF Pro Text
      fonts:
        - asset: fontsassets/SFProText/SF-Pro-Text-Light.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Medium.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Black.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Bold.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Heavy.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Regular.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Semibold.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Thin.otf
        - asset: fontsassets/SFProText/SF-Pro-Text-Ultralight.otf
          style: normal      
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages
flutter_intl:
  enabled: true

There are many ways you can do that but the easiest and the most elegant solution in my opinion is the following:有很多方法可以做到这一点,但我认为最简单和最优雅的解决方案如下:

Column(
  children: [
    Flexible(
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: 5,
        itemBuilder: (BuildContext context, int i) {
          return Text(i.toString());
        },
      ),
    ),
    ElevatedButton.icon(
        onPressed: null,
        icon: const Icon(Icons.abc),
        label: const Text('next')),
  ],
);

Start with a Column widget and then add ListView.builder and button of your choosing, I use ElevatedButton.icon , as it's children.Column小部件开始,然后添加ListView.builder和您选择的按钮,我使用ElevatedButton.icon ,因为它是孩子。 Now set the shrinkWrap setting in ListView.builder to true and finally wrap it with Flexible widget.现在将ListView.builder中的shrinkWrap设置为true ,最后用Flexible小部件包装它。

Hope this helped.希望这有帮助。

OK, this may not be the most elegant solution, but it does the trick.好的,这可能不是最优雅的解决方案,但它可以解决问题。

First create a Model for the button:首先为按钮创建一个Model

class BottomButton {
  final String? text;

  BottomButton({
    this.text,
  });
}

Then you are going to want to change all the output types from GymModel?那么您是否要更改 GymModel 中的所有 output 类型GymModel? to dynamic : dynamic

Widget _customDropDownExample(BuildContext context, dynamic item)

Widget _customPopupItemBuilderExample(
      BuildContext context, dynamic item, bool isSelected)

DropdownSearch<dynamic>

Future<List<dynamic>> getData(filter)

Now, in the _customDropDownExample function where you are checking if the item == null add another to see if item is !GymModel :现在,在_customDropDownExample function 中检查item == null添加另一个以查看item is !GymModel

if (item == null || item is !GymModel) {
  return const Text(
    "Выберите фитнесс центр",
    style: TextStyle(fontSize: 15, color: Color.fromRGBO(33, 33, 33, 0.6)),
  );
}

Inside _customPopupItemBuilderExample function check to see if the item is GymModel if so then do exactly what you were doing before and if not then create a button widget of your choice, I used an ElevatedButton for simplicity:_customPopupItemBuilderExample function 内部检查item is GymModel如果是,则完全按照您之前所做的操作,如果不是,则创建您选择的按钮小部件,为简单起见,我使用了ElevatedButton

if (item is GymModel) {
  return Container(
    decoration: !isSelected
        ? null
        : BoxDecoration(
            borderRadius: BorderRadius.circular(5),
            color: Colors.white,
          ),
    child: ListTile(
        selected: isSelected,
        title: Text(item.gym),
        subtitle: Text(item.location),
        leading: (item.avatar == "")
            ? const CircleAvatar(
                radius: 25,
                backgroundColor: Colors.white,
                backgroundImage: NetworkImage("https://picsum.photos/200"),
              )
            : CircleAvatar(
                radius: 25,
                backgroundImage: NetworkImage(item.avatar ?? ''),
              )),
  );
} else {
  return ElevatedButton(onPressed: () {}, child: Text(item.text));
}

Finally inside getData function where you check if the data != null add your button model ( BottomButton , in my case) to the list:最后在getData function 中检查data != null将按钮BottomButton (在我的情况下为底部按钮)添加到列表中:

Future<List<dynamic>> getData(filter) async {
  var response = await Dio().get(
    "https://my-json-server.typicode.com/dimapichuev2000/JSON_TEST/posts",
    queryParameters: {"filter": filter},
  );

  final data = response.data;
  if (data != null) {
    List<dynamic> res = GymModel.fromJsonList(data);
    BottomButton button = BottomButton(text: "Good Luck!");
    return [...res, button];
  }
  return [];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM