Flutter Desktop Skype Chat Box

Try recreating Skype Desktop’s chat box.

import 'dart:math';

import 'package:flutter/material.dart';

class Theme {
  static final Color primaryTextColor = Colors.grey.shade700;
  static final Color secondaryTextColor = Colors.grey;
}

class SkypePage extends StatefulWidget {
  @override
  _SkypePageState createState() => _SkypePageState();
}

class _SkypePageState extends State<SkypePage> {
  final double leftWidth = 300.0;

  Widget _leftCol() {
    return Padding(
      padding: const EdgeInsets.all(4.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              MainAvatarRow(),
            ],
          ),
          Row(
            children: [
              Flexible(
                flex: 8,
                child: TextField(
                  maxLines: 1,
                  decoration: InputDecoration(
                      contentPadding: EdgeInsets.all(0),
                      hintText: 'People, groups & messages',
                      prefixIcon: Icon(Icons.search),
                      border: OutlineInputBorder(
                          borderSide:
                              BorderSide(color: Theme.secondaryTextColor))),
                ),
              ),
              Flexible(
                  child: IconButton(
                      icon: Icon(Icons.dialpad_outlined), onPressed: () {}))
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              LabelButton(label: 'Chats', icon: Icons.message),
              LabelButton(label: 'Calls', icon: Icons.phone),
              LabelButton(label: 'Contacts', icon: Icons.contact_page_outlined),
              LabelButton(
                  label: 'Notifications',
                  icon: Icons.notification_important_outlined),
            ],
          ),
          Divider(),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              LabelButtonRow(
                  icon: Icons.video_call_outlined, label: 'Meet Now'),
              SizedBox(width: 4.0),
              LabelButtonRow(icon: Icons.note_add_outlined, label: 'New Chat')
            ],
          ),
          SizedBox(height: 8.0),
          Text(
            'CHATS',
            style: TextStyle(fontSize: 16.0, color: Theme.primaryTextColor),
          ),
          Expanded(
            child: ListView(
              children: [
                ...List.generate(15, (index) {
                  return ListTile(
                    contentPadding: EdgeInsets.zero,
                    title: Row(
                      children: [
                        AvatarRow(
                          name: 'Name ${index + 1}',
                          message: 'hello',
                        ),
                      ],
                    ),
                  );
                })
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _rightCol() {
    return Column(
      children: [
        Row(
          children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('Name 1',
                    style: TextStyle(
                        fontSize: 18.0, color: Theme.primaryTextColor)),
                Row(
                  children: [
                    Text('Last seen days ago',
                        style: TextStyle(color: Theme.secondaryTextColor)),
                    Text(' | ',
                        style: TextStyle(color: Theme.secondaryTextColor)),
                    Icon(Icons.image, color: Theme.secondaryTextColor),
                    Text('Gallery',
                        style: TextStyle(color: Theme.secondaryTextColor)),
                    Text(' | ',
                        style: TextStyle(color: Theme.secondaryTextColor)),
                    Icon(Icons.search, color: Theme.secondaryTextColor),
                    Text('Find',
                        style: TextStyle(color: Theme.secondaryTextColor)),
                  ],
                ),
              ],
            ),
            Spacer(),
            RoundButton(icon: Icons.video_call_outlined),
            RoundButton(icon: Icons.call),
            RoundButton(icon: Icons.person_add),
          ],
        ),
        Divider(
          color: Theme.secondaryTextColor,
          thickness: 1,
        ),
        SingleChildScrollView(
          child: ChatBox(),
        ),
        Row(
          children: [
            Flexible(
              flex: 8,
              child: TextField(
                maxLines: 1,
                decoration: InputDecoration(
                    fillColor: Colors.grey,
                    contentPadding: EdgeInsets.all(0),
                    hintText: 'Type a message',
                    prefixIcon: Icon(Icons.emoji_emotions_outlined),
                    border: OutlineInputBorder(
                        borderSide:
                            BorderSide(color: Theme.secondaryTextColor))),
              ),
            ),
            Spacer(),
            Flexible(child: RoundButton(icon: Icons.folder_open)),
            Flexible(child: RoundButton(icon: Icons.contact_mail_outlined)),
            Flexible(child: RoundButton(icon: Icons.mic)),
            Flexible(child: RoundButton(icon: Icons.more_horiz_outlined)),
          ],
        )
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
              width: leftWidth,
              height: double.infinity,
              decoration: BoxDecoration(
                  color: Colors.grey.shade100,
                  border: Border(
                      right: BorderSide(
                    color: Theme.secondaryTextColor,
                    width: 1,
                  ))),
              child: _leftCol()),
          Expanded(
            child: Container(
                padding: EdgeInsets.all(16.0),
                height: double.infinity,
                color: Colors.white,
                child: _rightCol()),
          ),
        ],
      ),
    );
  }
}

class Avatar extends StatelessWidget {
  final String? image;
  final double? size;

  const Avatar({Key? key, this.size, this.image}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        width: size ?? 36,
        height: size ?? 36,
        child: ClipRRect(
          borderRadius: BorderRadius.circular(36.0),
          child: Image.network(
            image ??
                'https://cdn.pixabay.com/photo/2014/11/30/14/11/cat-551554_960_720.jpg',
            fit: BoxFit.fill,
          ),
        ));
  }
}

class AvatarRow extends StatelessWidget {
  final Avatar? avatar;
  final String? name;
  final String? message;

  const AvatarRow({Key? key, this.name, this.message, this.avatar})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        //padding: const EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            avatar ?? Avatar(),
            SizedBox(width: 16),
            Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(name ?? 'Name',
                    style: TextStyle(color: Theme.primaryTextColor)),
                Text(message ?? 'hello',
                    style: TextStyle(color: Theme.secondaryTextColor)),
              ],
            ),
            Spacer(),
            Text('${Random().nextInt(12) + 1}/${Random().nextInt(20) + 1}/2021',
                style: TextStyle(color: Theme.secondaryTextColor))
          ],
        ),
      ),
    );
  }
}

class MainAvatarRow extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
        padding: const EdgeInsets.all(8.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Avatar(
                image:
                    'https://cdn.pixabay.com/photo/2014/04/13/20/49/cat-323262__340.jpg'),
            SizedBox(width: 16),
            Text('TL Templates',
                style: TextStyle(color: Theme.primaryTextColor)),
            SizedBox(width: 16),
            Text('\$0.00',
                style:
                    TextStyle(color: Theme.primaryTextColor.withOpacity(0.5))),
            Spacer(),
            IconButton(icon: Icon(Icons.more_horiz_outlined), onPressed: () {})
          ],
        ),
      ),
    );
  }
}

class LabelButton extends StatelessWidget {
  final IconData? icon;
  final VoidCallback? onPressed;
  final String? label;

  const LabelButton({Key? key, @required this.icon, this.onPressed, this.label})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        IconButton(
            icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
            onPressed: onPressed ?? () {}),
        Text(
          label!,
          style: TextStyle(color: Theme.primaryTextColor),
        )
      ],
    );
  }
}

class LabelButtonRow extends StatelessWidget {
  final IconData? icon;
  final VoidCallback? onPressed;
  final String? label;

  const LabelButtonRow(
      {Key? key, @required this.icon, this.onPressed, this.label})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.zero,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16.0),
      ),
      child: Row(
        children: [
          IconButton(
              icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
              onPressed: onPressed ?? () {}),
          Text(
            label!,
            style: TextStyle(color: Theme.primaryTextColor),
          ),
          IconButton(
              icon: Icon(Icons.keyboard_arrow_down_outlined,
                  color: Theme.primaryTextColor, size: 18.0),
              onPressed: onPressed ?? () {})
        ],
      ),
    );
  }
}

class RoundButton extends StatelessWidget {
  final IconData? icon;
  final VoidCallback? onPressed;

  const RoundButton({Key? key, @required this.icon, this.onPressed})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 0),
      child: Container(
        decoration:
            BoxDecoration(color: Colors.grey.shade100, shape: BoxShape.circle),
        child: IconButton(
            icon: Icon(icon!, color: Theme.primaryTextColor, size: 18.0),
            onPressed: onPressed ?? () {}),
      ),
    );
  }
}

class ChatBox extends StatefulWidget {
  @override
  _ChatBoxState createState() => _ChatBoxState();
}

class _ChatBoxState extends State<ChatBox> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: MediaQuery.of(context).size.height - 150,
      padding: EdgeInsets.fromLTRB(36.0, 16.0, 36.0, 16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Avatar(),
              SizedBox(width: 8.0),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Name, 8:30PM',
                    style: TextStyle(color: Theme.secondaryTextColor),
                  ),
                  Container(
                      padding: EdgeInsets.all(8.0),
                      decoration: BoxDecoration(
                          color: Colors.blueAccent,
                          borderRadius: BorderRadius.circular(8.0)),
                      child: Text('Hello\nMy test message')),
                ],
              ),
            ],
          ),
          SizedBox(height: 16.0),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Container(
                  padding: EdgeInsets.all(8.0),
                  decoration: BoxDecoration(
                      color: Colors.grey.shade200,
                      borderRadius: BorderRadius.circular(8.0)),
                  child: Text('Hello')),
            ],
          ),
        ],
      ),
    );
  }
}

Scroll to Top

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close