import 'package:cached_network_image/cached_network_image.dart'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/models/group_info_model.dart'; import 'package:chat/utils/CustomUI.dart'; import 'package:chat/utils/group_member_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class _MemberItem extends StatelessWidget { _MemberItem({ @required this.avatar, @required this.title, @required this.userId, this.groupTitle, this.onPressed, this.gradient, this.iconCode, this.isShowDivder: true, }); final int userId; final int iconCode; final String avatar; final String title; final String groupTitle; final VoidCallback onPressed; final Gradient gradient; final bool isShowDivder; static double _height(bool hasGroupTitle) { final _buttonHeight = MARGIN_VERTICAL * 2 + Constants.ContactAvatarSize + Constants.DividerWidth; if (hasGroupTitle) { return _buttonHeight + GROUP_TITLE_HEIGHT; } else { return _buttonHeight; } } @override Widget build(BuildContext context) { Widget _avatarIcon; if (iconCode == null) { _avatarIcon = ClipRRect( borderRadius: BorderRadius.circular(6), child: CachedNetworkImage( imageUrl: this.avatar, width: Constants.ContactAvatarSize, height: Constants.ContactAvatarSize, )); } else { _avatarIcon = Container( width: Constants.ContactAvatarSize, height: Constants.ContactAvatarSize, decoration: BoxDecoration( gradient: gradient, borderRadius: BorderRadius.circular(6)), child: Icon( IconData(this.iconCode, fontFamily: Constants.IconFontFamily), color: Colors.white, ), ); } Widget _button = Container( padding: const EdgeInsets.symmetric( vertical: MARGIN_VERTICAL, horizontal: 16.0), decoration: BoxDecoration(color: Colors.white), child: Row( children: [ _avatarIcon, SizedBox(width: 10.0), Text(title), ], ), ); //分组标签 Widget _itemBody; if (this.groupTitle != null) { _itemBody = Column( children: [ Container( height: GROUP_TITLE_HEIGHT, padding: EdgeInsets.only(left: 16.0, right: 16.0), color: const Color(AppColors.ContactGroupTitleBgColor), alignment: Alignment.centerLeft, child: Text(this.groupTitle, style: AppStyles.GroupTitleItemTextStyle), ), _button, ], ); } else { _itemBody = _button; } return InkWell( onTap: onPressed, child: Container( color: Colors.white, child: Column( children: [ _itemBody, isShowDivder ? Container( height: 1, color: const Color(0xFFF3F3F3), margin: EdgeInsets.only( left: 26 + Constants.ContactAvatarSize), ) : Container() ], ))); } } class AlterSelectPage extends StatefulWidget { final GroupInfoModel groupInfoModel; AlterSelectPage(this.groupInfoModel); @override _AlterSelectPageState createState() => _AlterSelectPageState(); static Future pickAlterUser( BuildContext context, GroupInfoModel groupInfoModel) async { var results = await Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { return AlterSelectPage(groupInfoModel); }, ), ); return results; } } class _AlterSelectPageState extends State { List members = []; //除自己外的其他成员 bool _hasdeleteIcon = false; //是否在搜索状态 TextEditingController _txtCtrl = TextEditingController(); ScrollController _scrollController = ScrollController(); List searchList = []; final Map _letterPosMap = {INDEX_BAR_WORDS[0]: 0.0}; FocusNode focusNode = FocusNode(); String _currentLetter = ''; @override void initState() { super.initState(); print('AlterSelectPage initState'); var myId = UserData().basicInfo.userId; for (var member in widget.groupInfoModel.members) { if (member.memberId != myId && member.inGroup > 0) { member.getNameTag(); members.add(member); } } } @override void dispose() { focusNode.unfocus(); _scrollController.dispose(); _txtCtrl.dispose(); super.dispose(); } updateIndexPos(List friendList) { //计算用于 IndexBar 进行定位的关键通讯录列表项的位置 double _totalPos = 0.0; for (var i = 0; i < friendList.length; i++) { bool _hasGroupTitle = true; if (i > 0 && friendList[i].nameTag.compareTo(friendList[i - 1].nameTag) == 0) { _hasGroupTitle = false; } if (_hasGroupTitle) { _letterPosMap[friendList[i].nameTag] = _totalPos; } _totalPos += _MemberItem._height(_hasGroupTitle); } } String getLetter(BuildContext context, double tileHeight, Offset globalPos) { RenderBox _box = context.findRenderObject(); var local = _box.globalToLocal(globalPos); int index = (local.dy ~/ tileHeight).clamp(0, INDEX_BAR_WORDS.length - 1); return INDEX_BAR_WORDS[index]; } void _jumpToIndex(String letter) { if (_letterPosMap.isNotEmpty) { final _pos = _letterPosMap[letter]; if (_pos != null) { _scrollController.animateTo(_letterPosMap[letter], curve: Curves.easeInOut, duration: Duration(microseconds: 200)); } } } Widget _buildIndexBar(BuildContext context, BoxConstraints constraints) { final List _letters = INDEX_BAR_WORDS.map((String word) { return Expanded( child: Container( margin: EdgeInsets.only(right: 5), decoration: BoxDecoration( shape: BoxShape.circle, color: _currentLetter == word ? Colors.blue : Colors.transparent, ), alignment: Alignment.center, padding: EdgeInsets.all(2), width: 20, child: Text( word, style: TextStyle( fontSize: 10, color: _currentLetter == word ? Colors.white : Colors.black), ))); }).toList(); final _totalHeight = constraints.biggest.height; final _tileHeight = _totalHeight / _letters.length; return GestureDetector( onVerticalDragDown: (DragDownDetails details) { setState(() { _currentLetter = getLetter(context, _tileHeight, details.globalPosition); _jumpToIndex(_currentLetter); }); }, onVerticalDragEnd: (DragEndDetails details) { setState(() { //_indexBarBgColor = Colors.transparent; _currentLetter = null; }); }, onVerticalDragCancel: () { setState(() { //_indexBarBgColor = Colors.transparent; _currentLetter = null; }); }, onVerticalDragUpdate: (DragUpdateDetails details) { setState(() { //var _letter = getLetter(context, _tileHeight, details.globalPosition); _currentLetter = getLetter(context, _tileHeight, details.globalPosition); _jumpToIndex(_currentLetter); }); }, child: Column( children: _letters, ), ); } @override Widget build(BuildContext context) { final List _body = []; if (!_hasdeleteIcon) { members.sort((a, b) => a.nameTag.compareTo(b.nameTag)); updateIndexPos(members); _body.addAll([ ListView.builder( controller: _scrollController, itemBuilder: (BuildContext context, int index) { bool _isGroupTitle = true; GroupMemberModel _contact = members[index]; if (index >= 1 && _contact.nameTag == members[index - 1].nameTag) { _isGroupTitle = false; } return _MemberItem( userId: _contact.memberId, avatar: _contact.avtar, title: _contact.refName, isShowDivder: _isGroupTitle, onPressed: () { Navigator.of(context).pop(_contact); }, groupTitle: _isGroupTitle ? _contact.nameTag : null); }, itemCount: members.length), Positioned( width: Constants.IndexBarWidth, right: 0.0, top: 0.0, bottom: 0.0, child: Container( child: LayoutBuilder( builder: _buildIndexBar, ), ), ) ]); } else { _body.add(ListView.builder( controller: _scrollController, itemBuilder: (BuildContext context, int index) { GroupMemberModel _contact = searchList[index]; return _MemberItem( userId: _contact.memberId, avatar: _contact.avtar, title: _contact.refName, isShowDivder: true, onPressed: () { Navigator.of(context).pop(_contact); }, groupTitle: null); }, itemCount: searchList.length, )); } if (_currentLetter != null && _currentLetter.isNotEmpty && !_hasdeleteIcon) { _body.add(Center( child: Container( width: Constants.IndexLetterBoxSize, height: Constants.IndexLetterBoxSize, decoration: BoxDecoration( color: AppColors.IndexLetterBoxBgColor, borderRadius: BorderRadius.all( Radius.circular(Constants.IndexLetterBoxRadius)), ), child: Center( child: Text(_currentLetter, style: AppStyles.IndexLetterBoxTextStyle), ), ), )); } return Scaffold( resizeToAvoidBottomPadding: false, backgroundColor: AppColors.NewAppbarBgColor, appBar: AppBar( title: Text(I18n.of(context).select_notice_people), centerTitle: true, elevation: 1, leading: CustomUI.buildCustomLeading(context), bottom: PreferredSize( preferredSize: Size.fromHeight(49), child: Container( alignment: Alignment.center, margin: EdgeInsets.only(bottom: 14, left: 12.5, right: 12.5), height: 35, decoration: BoxDecoration( color: const Color(0xFFEEEEEE), borderRadius: BorderRadius.all(Radius.circular(8))), child: TextField( keyboardAppearance: Brightness.light, keyboardType: TextInputType.text, textInputAction: TextInputAction.search, controller: _txtCtrl, cursorColor: Constants.BlueTextColor, maxLines: 1, style: TextStyle( textBaseline: TextBaseline.alphabetic, fontSize: 14.5), autofocus: false, inputFormatters: [ LengthLimitingTextInputFormatter(50), ], focusNode: focusNode, decoration: InputDecoration( hintText: I18n.of(context).search, hintStyle: TextStyle(fontSize: 14.5), prefixIcon: Icon( IconData( 0xe664, fontFamily: Constants.IconFontFamily, ), color: const Color(0xFFA0A0A0), size: 18, ), suffixIcon: Padding( padding: EdgeInsetsDirectional.only( start: 2.0, end: _hasdeleteIcon ? 20.0 : 0), child: _hasdeleteIcon ? new InkWell( onTap: (() { setState(() { WidgetsBinding.instance .addPostFrameCallback( (_) => _txtCtrl.clear()); _hasdeleteIcon = false; }); }), child: Icon( Icons.clear, size: 18.0, color: Constants.BlackTextColor, )) : new Text('')), filled: true, fillColor: Colors.transparent, border: InputBorder.none, ), onChanged: (str) async { setState(() { if (str.isEmpty) { _hasdeleteIcon = false; } else { _hasdeleteIcon = true; searchList = CustomUI().getSearchResult( str, members == null ? [] : members); } }); }, onEditingComplete: () {}), )), ), body: Stack( children: _body, )); } }