import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; typedef ChipBuilder = Widget Function(BuildContext context, T chip); typedef OnChipAdded = void Function(T chip); typedef OnChipRemoved = void Function(T chip); class ChipsInputTextField extends StatefulWidget { final TextEditingController controller; final FocusNode focusNode; final ValueChanged onInputChanged; final ChipBuilder chipBuilder; final OnChipAdded onChipAdded; final OnChipRemoved onChipRemoved; final String hint; const ChipsInputTextField({ Key key, @required this.chipBuilder, @required this.controller, this.onInputChanged, this.focusNode, this.onChipAdded, this.onChipRemoved, this.hint = 'Type a name', }) : super(key: key); @override ChipInputTextFieldState createState() => ChipInputTextFieldState(); } class ChipInputTextFieldState extends State> { final _chips = {}; bool _pauseItemAddition = false; void addItem(T item) { setState(() => _chips.add(item)); if (widget.onChipAdded != null) widget.onChipAdded(item); } void removeItem(T item) { setState(() { _chips.remove(item); if (_chips.isEmpty) resumeItemAddition(); }); if (widget.onChipRemoved != null) widget.onChipRemoved(item); } void pauseItemAddition() { if (!_pauseItemAddition) { setState(() => _pauseItemAddition = true); } widget.focusNode?.unfocus(); } void resumeItemAddition() { if (_pauseItemAddition) { setState(() => _pauseItemAddition = false); } widget.focusNode?.requestFocus(); } @override Widget build(BuildContext context) { return GestureDetector( onTap: _pauseItemAddition ? resumeItemAddition : null, child: Material( elevation: 1, color: Colors.white, child: Container( child: Padding( padding: const EdgeInsets.fromLTRB(16, 16, 16, 16), child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.baseline, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: Text( 'TO:', style: TextStyle( fontSize: 12, color: Colors.black.withOpacity(0.5), ), ), ), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Wrap( spacing: 8.0, runSpacing: 4.0, children: _chips.map((item) { return widget.chipBuilder(context, item); }).toList(), ), if (!_pauseItemAddition) TextField( controller: widget.controller, onChanged: widget.onInputChanged, focusNode: widget.focusNode, decoration: InputDecoration( isDense: true, border: InputBorder.none, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, contentPadding: const EdgeInsets.only(top: 4.0), hintText: widget.hint, hintStyle: TextStyle( color: Colors.black.withOpacity(0.5), fontSize: 14, ), ), ), ], ), ), SizedBox(width: 12), Align( alignment: Alignment.bottomCenter, child: IconButton( icon: _chips.isEmpty ? StreamSvgIcon.user( color: Colors.black.withOpacity(0.5), size: 24, ) : StreamSvgIcon.userAdd( color: Colors.black.withOpacity(0.5), size: 24, ), onPressed: resumeItemAddition, alignment: Alignment.topRight, visualDensity: VisualDensity.compact, padding: const EdgeInsets.all(0), splashRadius: 24, constraints: BoxConstraints.tightFor( height: 24, width: 24, ), ), ), ], ), ), ), ), ), ); } }