根据内容调整 SearchAnchor 建议列表的大小

问题描述 投票:0回答:1

我正在尝试将 SearchAnchor 的视图大小调整为其内容。假设我有一些基本代码(取自此处):https://api.flutter.dev/flutter/material/SearchAnchor-class.html。我不知道我需要提前显示多少建议(我首先需要进行 API 调用)。

import 'package:flutter/material.dart';

/// Flutter code sample for [SearchAnchor].

const Duration fakeAPIDuration = Duration(seconds: 1);

void main() => runApp(const SearchAnchorAsyncExampleApp());

class SearchAnchorAsyncExampleApp extends StatelessWidget {
  const SearchAnchorAsyncExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('SearchAnchor - async'),
        ),
        body: const Center(
          child: _AsyncSearchAnchor(),
        ),
      ),
    );
  }
}

class _AsyncSearchAnchor extends StatefulWidget {
  const _AsyncSearchAnchor();

  @override
  State<_AsyncSearchAnchor> createState() => _AsyncSearchAnchorState();
}

class _AsyncSearchAnchorState extends State<_AsyncSearchAnchor> {
  // The query currently being searched for. If null, there is no pending
  // request.
  String? _searchingWithQuery;

  // The most recent options received from the API.
  late Iterable<Widget> _lastOptions = <Widget>[];

  @override
  Widget build(BuildContext context) {
    return SearchAnchor(
        builder: (BuildContext context, SearchController controller) {
      return IconButton(
        icon: const Icon(Icons.search),
        onPressed: () {
          controller.openView();
        },
      );
    }, suggestionsBuilder:
            (BuildContext context, SearchController controller) async {
      _searchingWithQuery = controller.text;
      final List<String> options =
          (await _FakeAPI.search(_searchingWithQuery!)).toList();

      // If another search happened after this one, throw away these options.
      // Use the previous options instead and wait for the newer request to
      // finish.
      if (_searchingWithQuery != controller.text) {
        return _lastOptions;
      }

      _lastOptions = List<ListTile>.generate(options.length, (int index) {
        final String item = options[index];
        return ListTile(
          title: Text(item),
        );
      });

      return _lastOptions;
    });
  }
}

// Mimics a remote API.
class _FakeAPI {
  static const List<String> _kOptions = <String>[
    'aardvark',
    'bobcat',
    'chameleon',
  ];

  // Searches the options, but injects a fake "network" delay.
  static Future<Iterable<String>> search(String query) async {
    await Future<void>.delayed(fakeAPIDuration); // Fake 1 second delay.
    if (query == '') {
      return const Iterable<String>.empty();
    }
    return _kOptions.where((String option) {
      return option.contains(query.toLowerCase());
    });
  }
}

上面的代码可以运行,但是建议列表太大了。即使填满建议列表后,它仍然显示太多空白。我想在开始时显示一个较短(且空)的建议列表,并且在第一次 API 调用之后,我希望该列表调整其大小以适应其内容。

你知道该怎么做吗?

flutter searchbar
1个回答
0
投票

要实现您所描述的行为,您可以首先为建议列表设置固定高度,然后在 API 调用后根据内容动态调整高度。尝试这样的事情:

import 'package:flutter/material.dart';

const Duration fakeAPIDuration = Duration(seconds: 1);

void main() => runApp(const SearchAnchorAsyncExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('SearchAnchor - async'),
        ),
        body: const Center(
          child: _AsyncSearchAnchor(),
        ),
      ),
    );
  }
}

class _AsyncSearchAnchor extends StatefulWidget {
  const _AsyncSearchAnchor();

  @override
  State<_AsyncSearchAnchor> createState() => _AsyncSearchAnchorState();
}

class _AsyncSearchAnchorState extends State<_AsyncSearchAnchor> {
  String? _searchingWithQuery;
  late List<Widget> _lastOptions;

  @override
  Widget build(BuildContext context) {
    return SearchAnchor(
      builder: (BuildContext context, SearchController controller) {
        return IconButton(
          icon: const Icon(Icons.search),
          onPressed: () {
            controller.openView();
          },
        );
      },
      suggestionsBuilder: (BuildContext context, SearchController controller) async {
        _searchingWithQuery = controller.text;
        final List<String> options = (await _FakeAPI.search(_searchingWithQuery!)).toList();

        // If another search happened after this one, throw away these options.
        // Use the previous options instead and wait for the newer request to
        // finish.
        if (_searchingWithQuery != controller.text) {
          return _lastOptions;
        }

        _lastOptions = List<Widget>.generate(options.length, (int index) {
          final String item = options[index];
          return ListTile(
            title: Text(item),
          );
        });

        return _lastOptions.isNotEmpty
            ? _lastOptions
            : [Container(height: 50)]; // Set a fixed height initially
      },
    );
  }
}

class _FakeAPI {
  static const List<String> _kOptions = <String>[
    'aardvark',
    'bobcat',
    'chameleon',
  ];

  static Future<Iterable<String>> search(String query) async {
    await Future<void>.delayed(fakeAPIDuration); // Fake 1 second delay.
    if (query.isEmpty) {
      return const Iterable<String>.empty();
    }
    return _kOptions.where((String option) {
      return option.contains(query.toLowerCase());
    });
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.