chat_pickers 0.2.3 copy "chat_pickers: ^0.2.3" to clipboard
chat_pickers: ^0.2.3 copied to clipboard

A new Flutter package for displaying a keyboard for using emojis/gifs.

example/lib/main.dart

import 'package:bubble/bubble.dart';
import 'package:chat_pickers/chat_pickers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:giphy_client/giphy_client.dart';
import 'package:image_ink_well/image_ink_well.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';

import 'data/model/message.dart';
import 'data/repositories/chats_repository_impl.dart';
import 'data/state/chats_store.dart';
import 'utils/string_util.dart';
import 'widgets/auto_direction_rtl.dart';
import 'widgets/round_icon_button.dart';

class AppColors{
  AppColors._();

  static const colorPrimary = Color(0xFF075e55);
  static const colorPrimaryDark = Color(0xFF424242);
  static const colorAccent = Color(0xFF00cc3e);
}

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Chat app demo',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Provider(
      create: (_) => ChatsStore(ChatsRepositoryImpl()),
        child: HomeScreen(
          "Other User",
        ),
      ),
    );
  }
}

var currChatUid = "";    //??
class HomeScreen extends StatefulWidget {
  final String name;

  final String myUid = "me";
  final String otherUid = "other";


  HomeScreen(this.name);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen>  with SingleTickerProviderStateMixin{
  final TextEditingController _chatController = TextEditingController();
  final _scrollController = ScrollController();
  bool canSend = false;
  String _messageText = '';
  bool _isShowSticker = false;

  var _imageUrl;
  String _chatUid;
  bool _isBlocked = false;
  bool _first;

  String _nextUid;
  int _prevPos;
  bool _isRTL = false;

  // for state management
  ChatsStore _chatStore;
  List<ReactionDisposer> _disposers;


  AnimationController _animationController;

  GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();

  @override
  void initState() {
    super.initState();

    _chatUid = widget.myUid.compareTo(widget.otherUid) > 0
        ? "${widget.myUid}_${widget.otherUid}"
        : "${widget.otherUid}_${widget.myUid}";

    _first = true;

    _animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));

    _chatController.addListener(() {
        setState(() {

        });
    });

  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    _chatStore ??= Provider.of<ChatsStore>(context);

    _disposers ??= [
      reaction(
            (_) => _chatStore.errorMessage, (String message){
        _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message),));
      },),
    ];

    _chatStore.getMessages("chatId");

  }

  @override
  void dispose() {
    _disposers.forEach((disposer) => disposer());
    super.dispose();
  }

  void _handleSubmit() {
    final text =  _chatController.text;

    String content = text.trim();
    _chatController.clear();

    if (content.isEmpty) return;

    TextMessage message = TextMessage(
      text: content,
      senderId: widget.myUid,
      time: DateTime.now(),
    );

    _chatStore.addMessage(message);
  }



  _buildTextMessage(TextMessage message, bool isMe) {
    return Row(
      mainAxisAlignment: isMe
          ? (_isRTL) ? MainAxisAlignment.start : MainAxisAlignment.end
          : (_isRTL) ? MainAxisAlignment.end : MainAxisAlignment.start,
      children: <Widget>[

        Bubble(
          margin: BubbleEdges.only(top: 7, bottom: 7),
          alignment: Alignment.topRight,
          nip: isMe ? BubbleNip.rightTop : BubbleNip.leftTop,
          color: isMe ? Color.fromRGBO(225, 255, 199, 1.0): Colors.white,
          child: Row(
            crossAxisAlignment:
            CrossAxisAlignment.end,
//                : (_isRTL) ? CrossAxisAlignment.end : CrossAxisAlignment.start,
            children: <Widget>[
              Text(
                message.text,
                style: TextStyle(
                  color: Colors.black,
                  fontSize: 17.0,
                ),
              ),
              SizedBox(width: 8.0),
              Text(
                message.time == null ? "" : getInDayDate(message.time),
                textAlign: (isMe) ? TextAlign.left : TextAlign.right,
                style: TextStyle(
                  color: Colors.grey,
                  fontSize: 13.0,
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  _buildPhotoMessage(PhotoMessage message, bool isMe) {

    return Row(
      mainAxisAlignment: isMe
          ? (_isRTL) ? MainAxisAlignment.start : MainAxisAlignment.end
          : (_isRTL) ? MainAxisAlignment.end : MainAxisAlignment.start,

      children: <Widget>[

        Bubble(
          margin: BubbleEdges.only(top: 7, bottom: 7),
          alignment: Alignment.topRight,
          nip: isMe ? BubbleNip.rightTop : BubbleNip.leftTop,
          color: isMe ? Color.fromRGBO(225, 255, 199, 1.0): Colors.white,
          child: Column(
            crossAxisAlignment: isMe
                ? (_isRTL) ? CrossAxisAlignment.start : CrossAxisAlignment.end
                : (_isRTL) ? CrossAxisAlignment.end : CrossAxisAlignment.start,
            children: <Widget>[
              Material(
                child: InkWell(
                  child: (message.isGif != null && message.isGif)
                      ? Image.network(
                    message.url,
                    width: 160,
                    height: 160,
                    fit: BoxFit.cover,
                  )
                      : Image.network(
                    message.url ?? "",
                    width: 160,
                    height: 160,
                    fit: BoxFit.cover,
//                    placeholder: (context, url) => Container(
//                      color: AppColors.colorPrimary,
//                      height: 160,
//                      width: 160,
//                      child: Center(
//                          child: CircularProgressIndicator(
//                            valueColor:
//                            AlwaysStoppedAnimation<Color>(Colors.white),
//                          )),
//                    ),
                  ),
                  onTap: () {
                    // open image in big
                    var imageDialog = AlertDialog(
                      content: Container(
                        child: Image.network(
                          message.url ?? "",
                          fit: BoxFit.cover,
//                          placeholder: (context, url) => Container(
//                            width: 160,
//                            height: 160,
//                            child: CircularProgressIndicator(),
//                          ),
                        ),
                      ),
                    );

                    showDialog(context: context, builder: (_) => imageDialog);
                  },
                ),
              ),
              SizedBox(height: 6),
              Row(
                crossAxisAlignment:
                CrossAxisAlignment.end,
//                : (_isRTL) ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                children: <Widget>[
//              Text(
//                message.text,
//                style: TextStyle(
//                  color: Colors.black,
//                  fontSize: 17.0,
//                ),
//              ),
                  SizedBox(width: 8.0),
                  Text(
                    message.time == null ? "" : getInDayDate(message.time),
                    textAlign: (isMe) ? TextAlign.left : TextAlign.right,
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 13.0,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ],
    );

  }

  _buildDateDivider(Message message) {
    return Stack(
      alignment: Alignment.center,
      children: <Widget>[
        Bubble(
          alignment: Alignment.center,
          color: Color.fromRGBO(212, 234, 244, 1.0),
          child: Text(dateToSimpleString(message.time), textAlign: TextAlign.center, style: TextStyle(fontSize: 13.0)),
        ),
      ],
    );
  }

  _buildMessage(Message message, bool isMe) {
    var msg;
    var deleteMessageTitle = "";
    if (message is TextMessage) {
      msg = _buildTextMessage(message, isMe);
//      deleteMessageTitle = AppLocalizations()
//          .translate("delete_message", args: [message.text]);
    } else if (message is PhotoMessage) {
      msg = _buildPhotoMessage(message, isMe);
//      deleteMessageTitle = AppLocalizations().translate("delete_photo");
    }

    final msg2 = InkWell(
      child: msg,
      onLongPress: () {

      },
    );

    if (isMe) {
      return msg2;
    }
    return Column(
      children: <Widget>[
        msg2,
      ],
    );

  }


  Widget body(BuildContext context){
    return Observer(builder: (context) {
      switch(_chatStore.state){
        case StoreState.initial:
          return Expanded(child: Container());

        case StoreState.loading:
          return buildLoader();

        case StoreState.loaded:
          return buildData(context, _chatStore.messages);

        default:
          return Container();
      }
    });
  }


  Widget buildData(BuildContext context, List<Message> messages) {
    if(messages == null){
      return Expanded(
        child: Container(
            color: Colors.grey[900],
            child: Center(child: CircularProgressIndicator()),
        ),
      );
    }

    messages = messages.reversed.toList();
    DateTime lastDateTime = messages[0].time;
    bool first = true;

    return Expanded(
      child: Container(
        color: Colors.grey[900],
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 8),
          child: NotificationListener<ScrollNotification>(
            //onNotification: _handleScrollNotification,
            child: ListView.builder(
              reverse: true,
              padding: const EdgeInsets.only(top: 15.0),
              controller: _scrollController,
              itemCount: messages.length,//calculateListItemCount(_messages, isFinishedLoading),
              itemBuilder: (BuildContext context, int index) {
//                if (index >= _messages.length && !isFinishedLoading)
//                  return buildLoader();

                final Message message = messages[index];
                final bool isMe =  message.senderId == "me"; // message.senderId == widget.myUid;

                if (index + 1 < messages.length &&
                    messages[index].time.day != messages[index + 1].time.day &&
                    first) {
                  lastDateTime = message.time;
                  first = false;
                  return Column(
                    children: <Widget>[
                      _buildDateDivider(message),
                      _buildMessage(message, isMe)
                    ],
                  );
                } else if (message.time.day != messages.last.time.day &&
                    message.time.day < lastDateTime.day) {
                  lastDateTime = message.time;

                  return Column(
                    children: <Widget>[
                      _buildDateDivider(message),
                      _buildMessage(message, isMe)
                    ],
                  );
                } else if (index == messages.length - 1) {
                  return Column(
                    children: <Widget>[
                      _buildDateDivider(message),
                      _buildMessage(message, isMe),
                    ],
                  );
                }

                return _buildMessage(message, isMe);
              },
            ),
          ),
        ),
      ),
    );
  }

  Widget buildLoader() {
    return Expanded(
      child: Container(
        color: Colors.grey[900],
        child: Center(
          child: Container(
            child: CircularProgressIndicator(),
          ),
        ),
      ),
    );
  }

  Widget _buildEmptyMessage() {
    return Expanded(
      child: Container(
        color: Colors.grey[300],
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Icon(
                Icons.mode_comment,
                size: 100,
                color: Colors.red[200],
              ),
              SizedBox(height: 14),
              Text(
                "no chat",
                //AppLocalizations().translate("msg_no_chat"),
                textAlign: TextAlign.center,
                style: TextStyle(
                    fontWeight: FontWeight.w700,
                    fontSize: 20,
                    color: Colors.black
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildMessageComposer() {
    return Row(
      children: <Widget>[
        Expanded(
          child: Container(
            alignment: AlignmentDirectional.topStart,
            decoration: BoxDecoration(
                color: Colors.white,
                shape: BoxShape.rectangle,
                borderRadius: BorderRadius.circular(24)
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[

                Row(
                  children: <Widget>[
                    IconButton(
                      icon: _isShowSticker ? Icon(Icons.keyboard) : Icon(Icons.insert_emoticon),
                      iconSize: 24,
                      color: Colors.grey[600],
                      onPressed: ()  {
                        setState(() {
                          _isShowSticker
                              ? SystemChannels.textInput.invokeMethod('TextInput.show')
                              : SystemChannels.textInput.invokeMethod('TextInput.hide');

                          _isShowSticker = !_isShowSticker;
                        });
                      },
                    ),
                  ],
                ),
                Expanded(
                  child: AutoDirectionRTL(
                    isRTL: _isRTL,
                    text: _messageText.isNotEmpty ? _messageText : _isRTL ? "א" : "a",
                    child: Container(
                      child: TextField(
                        keyboardType: TextInputType.multiline,
                        maxLines: null,
                        textCapitalization: TextCapitalization.sentences,
                        onTap: () {
                          setState(() {
                            _isShowSticker = false;
                          });
                        },
                        onChanged: (value) {
                          _messageText = value;
                          setState(() {
                            canSend = value.trim().isNotEmpty;
                          });
                        },
                        controller: _chatController,
                        // onSubmitted: _handleSubmit,
                        decoration: InputDecoration(
                          border: InputBorder.none,
                          isDense: true,
                          hintText: "Type a message",
                        ),
                      ),
                    ),
                  ),
                ),
                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 10),
                  child: Row(
                    children: <Widget>[
                      Material(
                        child: InkWell(
                          child: RotationTransition(
                            turns: const AlwaysStoppedAnimation(45 / 360),
                            child: Icon(Icons.attach_file, color: Colors.grey[600], size: 24),
                          ),
                          onTap: (){

                          },
                        ),
                      ),
                      SizedBox(width: 14),
                      Material(
                        child: InkWell(
                          child: Icon(Icons.camera_alt, color: Colors.grey[600], size: 24),
                          onTap: (){

                          },
                        ),
                      ),
                    ],

                  ),
                ),

              ],
            ),
          ),
        ),
     //   _chatController.text.isEmpty
        SizedBox(width: 6),
        RoundIconButton(
          Icons.send,
          backgroundColor: Colors.grey[600],
          iconColor: Colors.white,
          size: 50,
          iconSize: 20,
          onPressed: _chatController.text.isNotEmpty? _handleSubmit : null,
        ),

      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      top: false,
      child: Scaffold(
        key: _scaffoldKey,
        backgroundColor: Theme.of(context).primaryColor,
        appBar: AppBar(
          title: Text(widget.name),
          leading: _backButtonWidget(),
        ),
        body: GestureDetector(
          onTap: () => FocusScope.of(context).unfocus(),
          child: Container(
            color: Colors.grey[900],
            child: Column(
              children: <Widget>[
                body(context),
                Container(
                    color: Colors.grey[900],
                    child: _buildMessageComposer()),
                SingleChildScrollView(
                  child: Container(
                    height: !_isShowSticker ? 0 : 270,
                    child: ChatPickers(
                      chatController: _chatController,
                      emojiPickerConfig: EmojiPickerConfig(
                          columns: 8,
                          numRecommended: 10,
                          bgBarColor: Colors.black,
                          bgColor: Colors.grey[900],
//                          indicatorColor: Colors.yellow,
                      ),
                      giphyPickerConfig: GiphyPickerConfig(
                          apiKey: "q3KulxGCIKWrOU283I3xM3DWvMnO5zOV",
                          onSelected: (gif){
                            _addGifMessage(gif);
                          }

                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }



  bool _handleScrollNotification(ScrollNotification notification) {
    // if we reached the start of the ListView
    if (notification is ScrollEndNotification &&
        _scrollController.position.extentAfter == 0) {
//      _chatBloc.getNextListPage(_chatUid,
//          nextMessageId: _nextUid, lastMessagePos: _prevPos);
    }

    return false;
  }

  int calculateListItemCount(
      List<Message> listItems, bool hasReachedEndOfResults) {
    return (hasReachedEndOfResults)
        ? listItems.length
        : listItems.length + 1; // +1 for loading indicator
  }

  Widget _buildProfileImage(String imageUrl) {
    var image = (imageUrl == null || imageUrl.contains('null'))
        ? ExactAssetImage("assets/images/blank_profile.png")
        : NetworkImage(imageUrl);

    var circleImageInkWell = CircleImageInkWell(
      onPressed: () {
        // fill me
      },
      size: 32,
      image: image,
      splashColor: Colors.white24,
    );

    return circleImageInkWell;
  }

  _backButtonWidget() {
    return Container(
      padding: const EdgeInsets.symmetric(vertical: 10),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(134.0)),
        shape: BoxShape.rectangle,
      ),
      child: Material(
        borderRadius: BorderRadius.all(Radius.circular(34.0)),
        color: Colors.transparent,
        child: InkWell(
          child: Row(
            children: <Widget>[
              Icon(Icons.arrow_back),
              _buildProfileImage(null),
            ],
          ),
          onTap: (){
       ///     Navigator.pop(context);
          },
        ),
      ),
    );
  }

  void _addGifMessage(GiphyGif gif) {
    PhotoMessage message = PhotoMessage(
      isGif: true,
      url: gif.images.original.url,
      senderId: widget.myUid,
      time: DateTime.now(),
    );

    _chatStore.addMessage(message);
  }
}