栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何在Flutter中建立芯片输入字段?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何在Flutter中建立芯片输入字段?

您可以在此处找到Chip Input Field类型小部件的实现:

最新的:https
:
//gist.github.com/slightfoot/c6c0f1f1baca326a389a9aec47886ad6

import 'dart:async';import 'package:flutter/material.dart';import 'package:flutter/services.dart';// See: https://twitter.com/shakil807/status/1042127387515858949// https://github.com/pchmn/MaterialChipsInput/tree/master/library/src/main/java/com/pchmn/materialchips// https://github.com/BelooS/ChipsLayoutManagervoid main() => runApp(ChipsDemoApp());class ChipsDemoApp extends StatelessWidget {  @override  Widget build(BuildContext context) {    return MaterialApp(      theme: ThemeData(        primaryColor: Colors.indigo,        accentColor: Colors.pink,      ),      home: DemoScreen(),    );  }}class DemoScreen extends StatefulWidget {  @override  _DemoScreenState createState() => _DemoScreenState();}class _DemoScreenState extends State<DemoScreen> {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text('Material Chips Input'),      ),      body: Column(        crossAxisAlignment: CrossAxisAlignment.stretch,        children: <Widget>[          Padding( padding: const EdgeInsets.all(8.0), child: TextField(   decoration: const InputDecoration(hintText: 'normal'), ),          ),          Expanded( child: Padding(   padding: const EdgeInsets.all(8.0),   child: ChipsInput<AppProfile>(     decoration: InputDecoration(prefixIcon: Icon(Icons.search), hintText: 'Profile search'),     findSuggestions: _findSuggestions,     onChanged: _onChanged,     chipBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {       return InputChip(         key: ObjectKey(profile),         label: Text(profile.name),         avatar: CircleAvatar(backgroundImage: NetworkImage(profile.imageUrl),         ),         onDeleted: () => state.deleteChip(profile),         onSelected: (_) => _onChipTapped(profile),         materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,       );     },     suggestionBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {       return ListTile(         key: ObjectKey(profile),         leading: CircleAvatar(backgroundImage: NetworkImage(profile.imageUrl),         ),         title: Text(profile.name),         subtitle: Text(profile.email),         onTap: () => state.selectSuggestion(profile),       );     },   ), ),          ),        ],      ),    );  }  void _onChipTapped(AppProfile profile) {    print('$profile');  }  void _onChanged(List<AppProfile> data) {    print('onChanged $data');  }  Future<List<AppProfile>> _findSuggestions(String query) async {    if (query.length != 0) {      return mockResults.where((profile) {        return profile.name.contains(query) || profile.email.contains(query);      }).toList(growable: false);    } else {      return const <AppProfile>[];    }  }}// -------------------------------------------------const mockResults = <AppProfile>[  AppProfile('Stock Man', 'stock@man.com', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),  AppProfile('Paul', 'paul@google.com', 'https://mbtskoudsalg.com/images/person-stock-image-png.png'),  AppProfile('Fred', 'fred@google.com',      'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),  AppProfile('Bera', 'bera@flutter.io',      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),  AppProfile('John', 'john@flutter.io',      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),  AppProfile('Thomas', 'thomas@flutter.io',      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),  AppProfile('Norbert', 'norbert@flutter.io',      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),  AppProfile('Marina', 'marina@flutter.io',      'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),];class AppProfile {  final String name;  final String email;  final String imageUrl;  const AppProfile(this.name, this.email, this.imageUrl);  @override  bool operator ==(Object other) =>      identical(this, other) || other is AppProfile && runtimeType == other.runtimeType && name == other.name;  @override  int get hashCode => name.hashCode;  @override  String toString() {    return 'Profile{$name}';  }}// -------------------------------------------------typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);typedef ChipSelected<T> = void Function(T data, bool selected);typedef ChipsBuilder<T> = Widget Function(BuildContext context, ChipsInputState<T> state, T data);class ChipsInput<T> extends StatefulWidget {  const ChipsInput({    Key key,    this.decoration = const InputDecoration(),    @required this.chipBuilder,    @required this.suggestionBuilder,    @required this.findSuggestions,    @required this.onChanged,    this.onChipTapped,  }) : super(key: key);  final InputDecoration decoration;  final ChipsInputSuggestions findSuggestions;  final ValueChanged<List<T>> onChanged;  final ValueChanged<T> onChipTapped;  final ChipsBuilder<T> chipBuilder;  final ChipsBuilder<T> suggestionBuilder;  @override  ChipsInputState<T> createState() => ChipsInputState<T>();}class ChipsInputState<T> extends State<ChipsInput<T>> implements TextInputClient {  static const kObjectReplacementChar = 0xFFFC;  Set<T> _chips = Set<T>();  List<T> _suggestions;  int _searchId = 0;  FocusNode _focusNode;  TextEditingValue _value = TextEditingValue();  TextInputConnection _connection;  String get text => String.fromCharCodes(        _value.text.preUnits.where((ch) => ch != kObjectReplacementChar),      );  bool get _hasInputConnection => _connection != null && _connection.attached;  void requestKeyboard() {    if (_focusNode.hasFocus) {      _openInputConnection();    } else {      FocusScope.of(context).requestFocus(_focusNode);    }  }  void selectSuggestion(T data) {    setState(() {      _chips.add(data);      _updateTextInputState();      _suggestions = null;    });    widget.onChanged(_chips.toList(growable: false));  }  void deleteChip(T data) {    setState(() {      _chips.remove(data);      _updateTextInputState();    });    widget.onChanged(_chips.toList(growable: false));  }  @override  void initState() {    super.initState();    _focusNode = FocusNode();    _focusNode.addListener(_onFocusChanged);  }  void _onFocusChanged() {    if (_focusNode.hasFocus) {      _openInputConnection();    } else {      _closeInputConnectionIfNeeded();    }    setState(() {      // rebuild so that _TextCursor is hidden.    });  }  @override  void dispose() {    _focusNode?.dispose();    _closeInputConnectionIfNeeded();    super.dispose();  }  void _openInputConnection() {    if (!_hasInputConnection) {      _connection = TextInput.attach(this, TextInputConfiguration());      _connection.setEditingState(_value);    }    _connection.show();  }  void _closeInputConnectionIfNeeded() {    if (_hasInputConnection) {      _connection.close();      _connection = null;    }  }  @override  Widget build(BuildContext context) {    var chipsChildren = _chips        .map<Widget>(          (data) => widget.chipBuilder(context, this, data),        )        .toList();    final theme = Theme.of(context);    chipsChildren.add(      Container(        height: 32.0,        child: Row(          mainAxisSize: MainAxisSize.min,          crossAxisAlignment: CrossAxisAlignment.stretch,          children: <Widget>[ Text(   text,   style: theme.textTheme.subhead.copyWith(     height: 1.5,   ), ), _TextCaret(   resumed: _focusNode.hasFocus, ),          ],        ),      ),    );    return Column(      crossAxisAlignment: CrossAxisAlignment.stretch,      //mainAxisSize: MainAxisSize.min,      children: <Widget>[        GestureDetector(          behavior: HitTestBehavior.opaque,          onTap: requestKeyboard,          child: InputDecorator( decoration: widget.decoration, isFocused: _focusNode.hasFocus, isEmpty: _value.text.length == 0, child: Wrap(   children: chipsChildren,   spacing: 4.0,   runSpacing: 4.0, ),          ),        ),        Expanded(          child: ListView.builder( itemCount: _suggestions?.length ?? 0, itemBuilder: (BuildContext context, int index) {   return widget.suggestionBuilder(context, this, _suggestions[index]); },          ),        ),      ],    );  }  @override  void updateEditingValue(TextEditingValue value) {    final oldCount = _countReplacements(_value);    final newCount = _countReplacements(value);    setState(() {      if (newCount < oldCount) {        _chips = Set.from(_chips.take(newCount));      }      _value = value;    });    _onSearchChanged(text);  }  int _countReplacements(TextEditingValue value) {    return value.text.preUnits.where((ch) => ch == kObjectReplacementChar).length;  }  @override  void performAction(TextInputAction action) {    _focusNode.unfocus();  }  void _updateTextInputState() {    final text = String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));    _value = TextEditingValue(      text: text,      selection: TextSelection.collapsed(offset: text.length),      composing: TextRange(start: 0, end: text.length),    );    _connection.setEditingState(_value);  }  void _onSearchChanged(String value) async {    final localId = ++_searchId;    final results = await widget.findSuggestions(value);    if (_searchId == localId && mounted) {      setState(() => _suggestions = results.where((profile) => !_chips.contains(profile)).toList(growable: false));    }  }}class _TextCaret extends StatefulWidget {  const _TextCaret({    Key key,    this.duration = const Duration(milliseconds: 500),    this.resumed = false,  }) : super(key: key);  final Duration duration;  final bool resumed;  @override  _TextCursorState createState() => _TextCursorState();}class _TextCursorState extends State<_TextCaret> with SingleTickerProviderStateMixin {  bool _displayed = false;  Timer _timer;  @override  void initState() {    super.initState();    _timer = Timer.periodic(widget.duration, _onTimer);  }  void _onTimer(Timer timer) {    setState(() => _displayed = !_displayed);  }  @override  void dispose() {    _timer.cancel();    super.dispose();  }  @override  Widget build(BuildContext context) {    final theme = Theme.of(context);    return FractionallySizedBox(      heightFactor: 0.7,      child: Opacity(        opacity: _displayed && widget.resumed ? 1.0 : 0.0,        child: Container(          width: 2.0,          color: theme.primaryColor,        ),      ),    );  }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/406920.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号