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