Pen Settings

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

Flutter

              
                import 'dart:html';
import 'package:flutter/material.dart';

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

class SlackApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: 'Slack', home: HomeScreen(),
    debugShowCheckedModeBanner: false);
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    final isMobile = MediaQuery.of(context).size.width < 700;
    return Material(
      color: Color(0xFF1D2229),
      child: Column(
        children: [if (!isMobile) WindowSection(), Expanded(child: WorkspaceSection())],
      ),
    );
  }
}

class WindowSection extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return Container(
      height: 34,
      width: size.width,
      color: Color(0xFF0A151F),
      child: Row(
        children: [
          SizedBox(width: 20),
          ClipOval(child: Container(color: Colors.red, width: 12, height: 12)),
          SizedBox(width: 10),
          ClipOval(child: Container(color: Colors.yellow, width: 12, height: 12)),
          SizedBox(width: 10),
          ClipOval(child: Container(color: Colors.green, width: 12, height: 12)),
          Expanded(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SizedBox(width: 15),
                Icon(Icons.arrow_back, color: Colors.grey[300], size: 20),
                SizedBox(width: 10),
                Icon(Icons.arrow_forward, color: Colors.grey[600], size: 20),
                SizedBox(width: 20),
                Icon(Icons.access_time, color: Colors.grey[300], size: 22),
                SizedBox(width: 40),
                Flexible(
                  child: Padding(
                    padding: const EdgeInsets.only(right: 15),
                    child: ConstrainedBox(
                      constraints: BoxConstraints(maxWidth: 500, minWidth: 0),
                      child: Container(
                        height: 25,
                        margin: const EdgeInsets.symmetric(vertical: 6),
                        decoration: BoxDecoration(
                            color: Color(0xFF262D31),
                            borderRadius: BorderRadius.circular(4),
                            border: Border.all(color: Color(0xFF42484B), width: 1)),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Icon(Icons.search, size: 14, color: Colors.grey[300]),
                            SizedBox(width: 5),
                            Container(
                              constraints: BoxConstraints(maxWidth: 150),
                              child: Text(
                                'Search on this workspace',
                                style: TextStyle(fontSize: 12, color: Colors.grey[500]),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
          Icon(Icons.help_outline, color: Colors.grey[400], size: 20),
          SizedBox(width: 20),
        ],
      ),
    );
  }
}

class WorkspaceSection extends StatefulWidget {
  @override
  WorkspaceSectionState createState() => WorkspaceSectionState();
}

class WorkspaceSectionState extends State<WorkspaceSection> with TickerProviderStateMixin {
  List<Widget> _workspacesItems = [];
  List<Tuple<Workspace, GlobalKey<WorkspaceItemState>>> _workspaces = [
    Tuple(
        Workspace()
          ..name = 'Flutter Study Group'
          ..url = 'flutterstudygroup.slack.com'
          ..logo =
              'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAw1BMVEX///97zfQTXJwRVZESWZgSWZYPUIkQUYsRVpMOTYRvyfMRVpIAS5QQU4+A0/kSWpoNSX0AVJjd7vgASpTV6vcAU5gATpUMR3qr3vi+5fnk8fnK6vq34/nT7fvr9/3b7feNqcivwtgAPXOO0/VZpNP2+fyVr8wAS4xhrdkAQ4DQ3Ohpt+Bch7To7vSR1fah2veBvuOhuNGww9jE0uJsj7hNfK4AP3opaaMAKWgAMnODocM7cagAPH3C0eJQfK52mb/d5e8O9u1tAAAG1ElEQVR4nO3da1vbNhgGYOwACTm5JouXFigwDqMcN6ddVxoa/v+vqiTbiZ04PsiSHsmXni/bR9/X++poh+7t2djY2NjY2NjY2NjY2NjY2NgU5vP5yccZzenJ2Wf0wwjP8emz06dxnPi/V7OzG/RTCcvZS0TbSL//fIJ+NBG5OXVydCvkzPR+vZnlVS+DfDHaeFrmY8aZsQPyvKA/s71q6HicVfMx47OBZbwZVwfSfEI/cN0c1/ORMhrWqWd1gYR4in7oOjmpDzSLyAU0icgJNGcscgMJ8Rj98FXSAEiIBqyLjYCOc4V+/tJwLBPZIn5EC0rSsIKUqPdRoznQcZ7RiKI0bVGW/jmasTtCgDpPNoKATv8MLdkRUUBtR6I4oKbTqUCgnjtwkUASNGc7goF97a40BAP1a1PRQO2WRPFAzQ5REoB6DUQZQK2uM6QAnf4M7VrlXArQcV7QsCRyKujoM5nKqqDjjNG0KPKAmuzbZAK1EEobg7oI/5AK1EAoGYifaSQD8auFbCB8xZcORB8QpQPRO2/5QPDpSQHQ6bcdCL0TVgJEvkRUAkReekvdbKfSdiCuSdW0KPAuURUQtmVTNQZhy70yIGoxVAcELRXqgKBRWPuLX/5gJlKFFcScm1QCIT2qsEUx+zWVQMg8qhSI+KRNKRAxy1igBWoO/KQSiDj1WqBIoG1RC7RAC2wd8HPbgXvqfCDgc9uByq62YS8olPlQwI/qrn5Br5haDxTxG8lqQNQnM1dtB96oegkK++hJUZMCv+qatR2oZjGEfpfXeqCKiQb7ZaUCIfjTUfnC8T9QoHzh+Hpy0Wrh+HrqBq9Qonyg6wYXrRVGQNcdIoky9zQJEEuUuC9dA6FEeVNNGkiItzCirPNhFoisoqQ2Hd9NPTdLhFVRinB87bsbQlwVZdy1je/+3vc2hbgqSgJu1RBHFP7rVwI8ONjPEcKIL+KB+wd5NcQRxQN3CVFEke8P6RgsEKKI4t4BE2CvUGg6kQKJcOc4ZMR/DSYyYJnQZCIBDga93kGJ0FwiAY4GSQ1z9jTGEwmw04mEZKY5KBKaSWRAJiQl3M/d0phNHN99GY3WNaQDUUci/zfCdAx2OilhSQ1dd2IWkVSwmxEWTaUmVpEBI+Ggag2NIkbAtLBkKk0aNTSESIHdEenS0aDTG/TYPFOhhJQ4N4I4fvjSPWQ1JFWkwopNSuIF7wYQSQUPYyFJxeVwRXQxP1Sv8/tDUsHDrJBNpVWGIY3/CBHWIDLgYbcbC9liUXkY0gSY2aZyo0ZAWsNotRhEE03VJqV9GoD+oEK1KhLgUUZYb6KJ+vQrRliJSIGRMGlTItxnR6fKQLJkvGtLZMC1kCwWyZ6tRglJn34DCUuJ44f/jtLCUTQMazYpyfAeRSz+ADwGMmFqGNImrbxWREGtGCXEBJjUkA7DHk+TkgRPMOLuRiXAD7GwGwnjbXf9JnXdKe4F8U5iCshqOEqvFZXOFel4Lk64o1HHD3/FwHSTxlu2GhuaJMN3zYgUuCXsJCen2k1K5hpgm+YRGTAjXG1K+UoIXBJziRHwQ36TcpXQ9abYfzkgSyTAP7eFndRyz5EAtuhvExPgWthNlnu+xZBleokVpojbwKRJB/VO99n4F2DhikiBecL1jo0LiNy4ZYkMmBqFR9kdG3cJXe8nGhgR18Bd8wxnCV3vO9q3R4kRcC08PBKyVERB82jO7hiwaKmovSNdFxGtY5n/v6OEneYl1EPIiIWjkLuEenQpyfxHvrDXuIQ6zDQs8x9FGzZuoBarRRxKzJybhIxC18Ov+KsQ4nYJe/VvSbPxsb+ryeZtEQO7qbWQ8+S7CnznncnbD4GHijjo09NGKHHVoyPOa+CNGmr1b+ft7YWL+PKiK6iEGk2lcd4W6yaNS9hgsdfheLiVcJG6YGu4UrjINxe7Ey6yryoaAV0fzckLIWaun1rWpDThYjRK3ts3A2KvvAsSLhrc46cDvg8uSLjgeG2fk+ANLdmZcCKiR/VbDFMJh/vNzr00Q8zHbRUTThruZkgJf6ERxQknDYHuBPeKu1rCYSOfG0BfHVbK5aQJUOtpJkkj4lT3HmW55G9U0EfCtcNNBH3LzhHORsX+0ZN64SIOdbpgKw3HogH+40O1c7/5VyJK4k20ukCskqfvfg2g72l4cVGa5bBqGb3gl2bXhxUzd6u1qq/XFXetXATlreoPl2YWMMrTclho9Pzh4zv6IRvm6cILdp2JvWC6fEc/oIjMv04Df0PpeX4QPIYm92c29xffiGjqR5mS//35+tYeXpyn+eXt63K5fL29fHtHP4yNjY2NjY2NjY2NjY2NjY0NOr8BRyPQkpP5WXoAAAAASUVORK5CYII='
          ..user = Utils.userLogged
          ..channels = [
            Channel()
              ..name = 'animations'
              ..private = false
              ..users = [
                Utils.userLogged,
              ]
              ..chats = [
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'Let me see if animated GIF works'
                  ..timestamp = 1590194974524,
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'https://media2.giphy.com/media/4PY2P7jVGwJpNb9TLl/giphy.gif'
                  ..timestamp = 1590194974550,
              ],
            Channel()
              ..name = 'announcements'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'Everyone! New clone ready! 🥳🥳🥳'
                  ..timestamp = 1590378680483,
                Chat()
                  ..user = Utils.martin
                  ..text = '🔥🔥🔥'
                  ..timestamp = 1590378685340,
                Chat()
                  ..user = Utils.simon
                  ..text = 'Flutter has no limits!'
                  ..timestamp = 1590378690209,
                Chat()
                  ..user = Utils.tim
                  ..text = '🙌 https://i1.wp.com/blog.codepen.io/wp-content/uploads/2020/03/flutter-on-codepen.png'
                  ..timestamp = 1590378826009,
                Chat()
                  ..user = Utils.nash
                  ..text = '👀👀👀'
                  ..timestamp = 1590378863226,
                Chat()
                  ..user = Utils.chris
                  ..text = 'How many pens so far?!'
                  ..timestamp = 1590380405755,
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'Like 19 #FlutterPen'
                  ..timestamp = 1590380435502,
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'or more... not sure lol Let me check'
                  ..timestamp = 1590380462853,
              ]
              ..topic = 'Announcements - Please use Threads for conversations in this channel.',
            Channel()
              ..name = 'community_organizers'
              ..private = true
              ..users = Utils.allUsers.sublist(0, 7)
              ..chats = [
                Chat()
                  ..user = Utils.nilay
                  ..text = 'If you need help for events and community related, make sure to contact me 😊'
                  ..timestamp = 1590380204601,
              ],
            Channel()
              ..name = 'firebase'
              ..private = false
              ..users = [Utils.userLogged, Utils.nash]
              ..chats = [],
            Channel()
              ..name = 'fuchsia'
              ..private = false
              ..users = [Utils.userLogged, Utils.nash]
              ..chats = [],
            Channel()
              ..name = 'general'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'This channel always gets so random, lol 😜'
                  ..timestamp = 1590380262195,
                Chat()
                  ..user = Utils.simon
                  ..text = 'Well... if you don\'t spam...'
                  ..timestamp = 1590380286760,
                Chat()
                  ..user = Utils.userLogged
                  ..text = '😒'
                  ..timestamp = 1590380312687,
                Chat()
                  ..user = Utils.nash
                  ..text = '😂😂😂😂😂'
                  ..timestamp = 1590380312687,
                Chat()
                  ..user = Utils.scott
                  ..text = 'You guys love to fight all the time lol'
                  ..timestamp = 1590380346349,
              ],
            Channel()
              ..name = 'hack20'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [
                Chat()
                  ..user = Utils.simon
                  ..text = 'Sign up now for #Hack20\n\nhttps://flutterhackathon.com/#/\n https://miro.medium'
                      '.com/max/2580/0*vValokazzfj52F2N?.jpg'
                ..timestamp = 1590379043156
              ],
            Channel()
              ..name = 'interact'
              ..private = true
              ..users = [Utils.userLogged, Utils.nash]
              ..chats = [],
            Channel()
              ..name = 'intros'
              ..private = false
              ..users = [Utils.userLogged, Utils.nash]
              ..chats = [],
            Channel()
              ..name = 'web'
              ..private = false
              ..users = [Utils.userLogged, Utils.nash]
              ..chats = [
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'CodePen continues to amaze me! https://miro.medium.com/max/800/1*Otx7CXIY9eh0Sxlp54olxA.png'
                  ..timestamp = 1590379697709
              ],
          ]
          ..users = Utils.allUsers
          ..dms = [
            DM()
              ..user = Utils.userLogged
              ..chats = [],
            DM()
              ..user = Utils.chris
              ..chats = [
                Chat()
                  ..user = Utils.chris
                  ..text = 'Sounds like you have something new, right?'
                  ..timestamp = 1590379519706
              ],
            DM()
              ..user = Utils.nilay
              ..chats = [],
            DM()
              ..user = Utils.tim
              ..chats = [
                Chat()
                  ..user = Utils.tim
                  ..text = 'Working on a new CodePen?'
                  ..timestamp = 1590366288580
              ],
            DM()
              ..user = Utils.nash
              ..chats = [
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'Hey, Nash! Are you there?'
                  ..timestamp = 1590199478148,
                Chat()
                  ..user = Utils.nash
                  ..text = 'Yep! Here, sup'
                  ..timestamp = 1590199490797,
                Chat()
                  ..user = Utils.nash
                  ..text = 'Just finished college!!!'
                  ..timestamp = 1590199970558,
                Chat()
                  ..user = Utils.userLogged
                  ..text = '🥳🥳🥳'
                  ..timestamp = 1590199975102,
              ],
            DM()
              ..user = Utils.simon
              ..chats = [
                Chat()
                  ..user = Utils.simon
                  ..text = 'Jump to Zoom. The whole gang is there.'
                  ..timestamp = 1590379563037,
                Chat()
                  ..user = Utils.userLogged
                  ..text = 'Gotcha! And thanks for all the help 😊'
                  ..timestamp = 1590379587572,
              ],
            DM()
              ..user = Utils.scott
              ..chats = [
                Chat()
                  ..user = Utils.scott
                  ..text = 'Did @Simon told you to jump into Zoom?'
                  ..timestamp = 1590379624392,
              ],
          ],
        GlobalKey<WorkspaceItemState>()),
    Tuple(
        Workspace()
          ..name = 'Flutter Bs As'
          ..url = 'flutterdevbsas.slack.com'
          ..user = Utils.userLogged
          ..channels = [
            Channel()
              ..name = 'random'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [],
            Channel()
              ..name = 'general'
              ..private = false
              ..users = Utils.allUsers.sublist(0, 5)
              ..chats = [],
            Channel()
              ..name = 'admin'
              ..private = true
              ..users = [
                Utils.userLogged,
                Utils.ariel
              ]
              ..chats = [],
          ]
          ..users = [
            Utils.userLogged,
            Utils.ariel,
          ]
          ..dms = [
            DM()
              ..user = Utils.userLogged
              ..chats = [],
            DM()
              ..user = Utils.ariel
              ..chats = [],
          ],
        GlobalKey<WorkspaceItemState>()),
    Tuple(
        Workspace()
          ..name = 'GDG Global'
          ..url = 'gdgglobal.slack.com'
          ..logo =
              'https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcRzlnlRY55ine9zQvxR2dCJQKxEvxLkKr5IWfPfEZlEPq33Iya3&usqp=CAU'
          ..user = Utils.userLogged
          ..channels = [
            Channel()
              ..name = 'io19'
              ..private = false
              ..users = Utils.allUsers.sublist(0, 6)
              ..chats = [],
            Channel()
              ..name = 'announcements'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [],
            Channel()
              ..name = 'events'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [],
            Channel()
              ..name = 'jobs'
              ..private = false
              ..users = Utils.allUsers.sublist(0, 3)
              ..chats = [],
            Channel()
              ..name = 'support'
              ..private = false
              ..users = Utils.allUsers
              ..chats = [],
          ]
          ..users = Utils.allUsers
          ..dms = [
            DM()
              ..user = Utils.userLogged
              ..chats = [],
          ],
        GlobalKey<WorkspaceItemState>()),
  ];

  List<DMUserItem> _dms = [];
  List<ChannelItem> _channels = [];

  List<Widget> _menus = [
    _menuSection(Icons.subject, 'All unreads', () {}),
    _menuSection(Icons.chat, 'Threads', () {}),
    _menuSection(Utils.at, 'Mentions & Reactions', () {}),
    _menuSection(Icons.drafts, 'Drafts', () {}),
    _menuSection(Icons.bookmark_border, 'Saved items', () {}),
    _menuSection(Utils.hash, 'Mentions & Reactions', () {}),
    _menuSection(Utils.at, 'Channel browser', () {}),
    _menuSection(Icons.book, 'People', () {}),
    _menuSection(Icons.apps, 'Apps', () {}),
    _menuSection(Icons.folder_open, 'Files', () {}),
  ];

  int _currentIndex = 0;

  ClickItem _clickItem;
  ClickItem _closeItem;
  ClickItem _chatsItem;
  ClickItem _chanlItem;

  bool _showDMs = true;
  bool _showMenus = false;
  bool _showChannels = true;

  final _textController = TextEditingController();

  AnimationController _leftController;
  AnimationController _rightController;
  Animation<Offset> _leftPanel;
  Animation<Offset> _middleLeftPanel;
  Animation<Offset> _middleRightPanel;
  Animation<Offset> _rightPanel;

  double _direction = 0;
  AnimationController _activePanel;

  bool isMobile = false;
  bool switchToMobile = false;

  void _closePanels() {
    if (_activePanel != null) {
      if (_activePanel.value < 0.5) {
        _activePanel.reverse();
      } else {
        _activePanel.forward();
      }
      _activePanel = null;
    }
  }

  void _itemSelection<T extends List>(T list, dynamic selected) {
    list.asMap().forEach((key, value) {
      if (list[key].key.currentState != null) {
        list[key].key.currentState.selected(selected is bool ? selected : key == selected);
      }
    });
  }

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

    _leftController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    _rightController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));

    _leftPanel = Tween<Offset>(begin: Offset(-1, 0), end: Offset.zero).animate(_leftController);
    _middleLeftPanel = Tween<Offset>(begin: Offset(0, 0), end: Offset(0.85, 0)).animate(_leftController);
    _middleRightPanel = Tween<Offset>(begin: Offset(0, 0), end: Offset(-0.15, 0)).animate(_rightController);
    _rightPanel = Tween<Offset>(begin: Offset(1, 0), end: Offset.zero).animate(_rightController);

    _clickItem = (index) {
      _workspacesItems.forEach((element) {
        if (element is WorkspaceItem) {
          (element.key as GlobalKey<WorkspaceItemState>).currentState.selected(index == element.index);
          setState(() => _currentIndex = index);
          _initChannels(true);
          _initDMs(true);
        }
      });
      if (isMobile) {
        _leftController.reverse();
        _direction = 1.0;
      }
    };
    _workspaces.asMap().forEach((index, value) {
      setState(() => _workspacesItems.add(WorkspaceItem(
          key: value.second, index: index, selected: index == 0, workspace: value.first, clickItem: _clickItem)));
    });
    _workspacesItems.add(AspectRatio(
        key: GlobalKey(), aspectRatio: 1, child: Container(child: Icon(Icons.add, color: Color(0xFFACADAF)))));

    _closeItem = (index) => Future.microtask(() => setState(() {
          _dms.removeAt(index);
          _workspaces[_currentIndex].first.dms.removeAt(index);
          _workspaces[_currentIndex].first.currentChannel = _workspaces[_currentIndex].first.currentChannel;
          _workspaces[_currentIndex].first.currentDM = -1;
          (_channels[_workspaces[_currentIndex].first.currentChannel].key as GlobalKey<ChannelItemState>)
              .currentState
              .selected(true);
          _dms.asMap().forEach((key, value) {
            (_dms[key].key as GlobalKey<DMUserItemState>).currentState.index(key);
          });
        }));
    _chatsItem = (index) => Future.microtask(() => setState(() {
          _workspaces[_currentIndex].first.currentDM = index;
          if (isMobile) {
            _direction = -1.0;
            _rightController.forward();
          } else {
            _itemSelection(_channels, false);
            _itemSelection(_dms, index);
          }
        }));
    _chanlItem = (index) => Future.microtask(() => setState(() {
          _workspaces[_currentIndex].first.currentChannel = index;
          _workspaces[_currentIndex].first.currentDM = -1;
          if (isMobile) {
            _direction = -1.0;
            _rightController.forward();
          } else {
            _itemSelection(_dms, false);
            _itemSelection(_channels, index);
          }
        }));

    _initDMs(false);
    _initChannels(false);

    _rightPanel.addStatusListener((status) {
      if (status == AnimationStatus.completed || status == AnimationStatus.dismissed) {
        FocusScope.of(context).requestFocus(FocusNode());
      }
    });

    _leftController.addListener(() {
      if (_leftController.value == 0.0 || _leftController.value == 1.0) {
        setState(() {});
      }
    });
  }

  @override
  void dispose() {
    _leftController.dispose();
    _rightController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    isMobile = size.width < 700;
    if (!isMobile) switchToMobile = false;
    if (!switchToMobile && isMobile) {
      switchToMobile = true;
      _itemSelection(_dms, false);
      _itemSelection(_channels, false);
    }
    return isMobile ? _mobileView(size) : _desktopView(size);
  }

  Widget _mobileView(Size size) {
    final channelIndex = _workspaces[_currentIndex].first.currentChannel;
    final channel = channelIndex > -1 ? _workspaces[_currentIndex].first.channels[channelIndex] : null;
    final dmIndex = _workspaces[_currentIndex].first.currentDM;
    final dm = dmIndex > -1 ? _workspaces[_currentIndex].first.dms[dmIndex] : null;
    return GestureDetector(
      onHorizontalDragUpdate: (details) {
        if (_activePanel == null) {
          if (_leftController.value > 0.0) {
            _activePanel = _leftController;
          } else if (_rightController.value > 0.0) {
            _activePanel = _rightController;
          } else {
            if (details.primaryDelta > 5) {
              _activePanel = _leftController;
              _direction = 1.0;
            } else if (details.primaryDelta < -5) {
              _activePanel = _rightController;
              _direction = -1.0;
            } else {
              return;
            }
          }
        }
        _activePanel.value += details.primaryDelta * _direction / context.size.width;
      },
      onHorizontalDragEnd: (_) => _closePanels(),
      onHorizontalDragCancel: () => _closePanels(),
      child: Stack(
        fit: StackFit.expand,
        children: [
          AnimatedBuilder(
            animation: Listenable.merge([_middleLeftPanel, _middleRightPanel]),
            builder: (BuildContext context, Widget child) {
              final value = _middleLeftPanel.value + _middleRightPanel.value;
              return SlideTransition(position: AlwaysStoppedAnimation(value), child: child);
            },
            child: Stack(
              children: [
                Scaffold(
                  backgroundColor: Color(0xFF1B1B24),
                  appBar: AppBar(
                    elevation: 0,
                    backgroundColor: Color(0xFF1A212E),
                    leading: GestureDetector(
                      onTap: () {
                        _leftController.forward();
                        _direction = 1.0;
                      },
                      child: Container(
                        padding: const EdgeInsets.all(12),
                        child: _workspaces[_currentIndex].first.logo != null
                            ? ClipRRect(
                                borderRadius: BorderRadius.circular(5),
                                child: Image.network(_workspaces[_currentIndex].first.logo),
                              )
                            : Container(
                                decoration: BoxDecoration(
                                  color: Colors.grey,
                                  borderRadius: BorderRadius.circular(5),
                                ),
                                child: Center(
                                  child: Text(
                                    _workspaces[_currentIndex].first.name.substring(0, 1),
                                    style: TextStyle(
                                      fontSize: 18,
                                      color: Colors.black,
                                      fontWeight: FontWeight.w600,
                                    ),
                                  ),
                                ),
                              ),
                      ),
                    ),
                    title: Text(_workspaces[_currentIndex].first.name),
                    centerTitle: false,
                    actions: [Icon(Icons.search, color: Colors.white), SizedBox(width: 20)],
                  ),
                  body: Column(
                    children: [
                      Separator(vertical: false),
                      Expanded(
                        child: ListView(
                          children: [
                            SizedBox(height: 15),
                            _searchBarSection(),
                            SizedBox(height: 15),
                            _itemSideMenu(Icons.chat, 'Threads'),
                            _titleSection(
                                'Channels', () => setState(() => _showChannels = !_showChannels), _showChannels, null),
                            if (_showChannels) ..._channels,
                            SizedBox(height: 5),
                            _titleSection(
                                'Direct messages', () => setState(() => _showDMs = !_showDMs), _showDMs, null),
                            if (_showDMs) ..._dms,
                            if (_showDMs) _itemSideMenu(Icons.add, 'Invite People'),
                            SizedBox(height: 15),
                          ],
                        ),
                      ),
                      Separator(vertical: false),
                    ],
                  ),
                  bottomNavigationBar: _bottomBar(),
                  floatingActionButton: FloatingActionButton(
                    elevation: 0,
                    child: Icon(Icons.edit, color: Color(0xFF1C2128), size: 18),
                    backgroundColor: Colors.white,
                    onPressed: () {},
                  ),
                ),
                GestureDetector(
                  onTap: _leftController.value == 1.0
                      ? () {
                          _leftController.reverse();
                          _direction = 1.0;
                        }
                      : null,
                  child: IgnorePointer(
                    ignoring: _leftController.value < 1.0,
                    child: AnimatedOpacity(
                      duration: Duration(milliseconds: 200),
                      opacity: _leftController.value > 0.7 ? 0.7 : 0.0,
                      child: Container(
                        color: Colors.black,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
          FractionallySizedBox(
            widthFactor: 0.85,
            alignment: Alignment.centerLeft,
            child: SlideTransition(
              position: _leftPanel,
              child: Row(
                children: [
                  SizedBox(width: 5),
                  Container(
                    width: 70,
                    height: size.height,
                    color: Color(0xFF22222B),
                    child: ReorderableListView(
                      padding: const EdgeInsets.only(top: 200),
                      onReorder: (index, destination) {
                        if (index != _workspacesItems.length - 1) {
                          Future.microtask(() {
                            setState(() {
                              final item = _workspacesItems.removeAt(index);
                              _workspacesItems.insert(destination, item);
                            });
                          });
                        }
                      },
                      children: _workspacesItems,
                      scrollDirection: Axis.vertical,
                    ),
                  ),
                  SizedBox(width: 5),
                  Expanded(
                    child: Container(
                      color: Color(0xFF1B1B24),
                      child: Center(
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Padding(
                              padding: const EdgeInsets.only(left: 15, right: 15),
                              child: Text(
                                _workspaces[_currentIndex].first.name,
                                textAlign: TextAlign.left,
                                style: TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.w600),
                              ),
                            ),
                            SizedBox(height: 2),
                            Padding(
                              padding: const EdgeInsets.only(left: 15, right: 15, bottom: 10),
                              child: Text(
                                _workspaces[_currentIndex].first.url,
                                textAlign: TextAlign.left,
                                style: TextStyle(color: Colors.grey, fontSize: 12),
                              ),
                            ),
                            SizedBox(height: 5),
                            Separator(vertical: false),
                            SizedBox(height: 10),
                            _itemSideMenu(Icons.search, 'Channel browser'),
                            SizedBox(height: 5),
                            _itemSideMenu(Icons.book, 'People'),
                            SizedBox(height: 10),
                            Separator(vertical: false),
                            SizedBox(height: 10),
                            _itemSideMenu(Icons.person_add, 'Invite people'),
                            SizedBox(height: 5),
                            _itemSideMenu(Icons.help_outline, 'Help'),
                            SizedBox(height: 10),
                            Separator(vertical: false),
                            SizedBox(height: 10),
                            _itemSideMenu(Icons.tune, 'Preferences'),
                            SizedBox(height: 5),
                            _itemSideMenu(Icons.input, 'Sign out of ${_workspaces[_currentIndex].first.name}'),
                          ],
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
          SlideTransition(
            position: _rightPanel,
            child: Container(
              color: Color(0xFF1D2229),
              child: dmIndex > -1 ? _dmsView(dm) : _channelView(channel),
            ),
          ),
        ],
      ),
    );
  }

  Widget _bottomBar() {
    return Theme(
      data: Theme.of(context).copyWith(canvasColor: Color(0xFF1A212E)),
      child: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(icon: SizedBox(height: 30, child: Icon(Icons.home)), title: Text('Home')),
          BottomNavigationBarItem(icon: SizedBox(height: 30, child: Icon(Icons.message)), title: Text('DMs')),
          BottomNavigationBarItem(icon: SizedBox(height: 30, child: Icon(Utils.at)), title: Text('Mentions')),
          BottomNavigationBarItem(icon: SizedBox(height: 30, child: Icon(Icons.insert_emoticon)), title: Text('You')),
        ],
        elevation: 0,
        currentIndex: 0,
        showUnselectedLabels: true,
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Colors.white,
        unselectedItemColor: Colors.grey[600],
        selectedLabelStyle: TextStyle(fontSize: 12),
        unselectedLabelStyle: TextStyle(fontSize: 12),
      ),
    );
  }

  Widget _desktopView(Size size) {
    final channelIndex = _workspaces[_currentIndex].first.currentChannel;
    final channel = channelIndex > -1 ? _workspaces[_currentIndex].first.channels[channelIndex] : null;
    final dmIndex = _workspaces[_currentIndex].first.currentDM;
    final dm = dmIndex > -1 ? _workspaces[_currentIndex].first.dms[dmIndex] : null;
    return Row(
      children: [
        Container(
          width: 55,
          height: size.height,
          color: Color(0xFF1D2229),
          child: ReorderableListView(
            padding: const EdgeInsets.only(top: 10),
            onReorder: (index, destination) {
              if (index != _workspacesItems.length - 1) {
                Future.microtask(() {
                  setState(() {
                    final item = _workspacesItems.removeAt(index);
                    _workspacesItems.insert(destination, item);
                  });
                });
              }
            },
            children: _workspacesItems,
            scrollDirection: Axis.vertical,
          ),
        ),
        Separator(vertical: true),
        Container(
          width: 260,
          height: size.height,
          child: Column(
            children: [
              Container(
                height: 65,
                width: 260,
                child: Row(
                  children: [
                    SizedBox(width: 15),
                    Expanded(
                      child: Column(
                        children: [
                          Row(
                            children: [
                              Text(_workspaces[_currentIndex].first.name,
                                  style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600)),
                              SizedBox(width: 4),
                              Icon(Icons.keyboard_arrow_down, color: Colors.white, size: 14),
                            ],
                          ),
                          SizedBox(height: 4),
                          Row(
                            crossAxisAlignment: CrossAxisAlignment.end,
                            children: [
                              SizedBox(height: 20, child: Utils.online(false)),
                              SizedBox(width: 4),
                              Text(_workspaces[_currentIndex].first.user.name,
                                  style: TextStyle(color: Colors.white.withOpacity(0.6), height: 1.2)),
                            ],
                          ),
                        ],
                        crossAxisAlignment: CrossAxisAlignment.start,
                        mainAxisAlignment: MainAxisAlignment.center,
                      ),
                    ),
                    Container(
                      width: 40,
                      height: 40,
                      decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)),
                      child: Icon(Icons.edit, color: Color(0xFF1C2128), size: 18),
                    ),
                    SizedBox(width: 15),
                  ],
                ),
              ),
              Separator(vertical: false),
              Expanded(
                child: ListView(
                  children: [
                    SizedBox(height: 15),
                    ..._menus.sublist(0, _showMenus ? _menus.length : 3),
                    _menuSection(_showMenus ? Icons.arrow_upward : Icons.arrow_downward, 'Show more',
                        () => setState(() => _showMenus = !_showMenus)),
                    SizedBox(height: 15),
                    Separator(vertical: false),
                    SizedBox(height: 5),
                    _titleSection(
                        'Channels', () => setState(() => _showChannels = !_showChannels), _showChannels, null),
                    if (_showChannels) ..._channels,
                    SizedBox(height: 5),
                    _titleSection('Direct messages', () => setState(() => _showDMs = !_showDMs), _showDMs, null),
                    if (_showDMs) ..._dms,
                    if (_showDMs) _itemSideMenu(Icons.add, 'Invite People'),
                    SizedBox(height: 15),
                  ],
                ),
              )
            ],
          ),
        ),
        Separator(vertical: true),
        Expanded(child: dmIndex > -1 ? _dmsView(dm) : _channelView(channel)),
      ],
    );
  }

  Widget _searchBarSection() {
    return Container(
      height: 35,
      margin: const EdgeInsets.symmetric(horizontal: 15),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(5),
        border: Border.all(color: Colors.grey[800], width: 0.5),
      ),
      child: Container(
        height: 35,
        alignment: Alignment.centerLeft,
        padding: const EdgeInsets.only(left: 10),
        child: Text('Jump to...', style: TextStyle(color: Colors.grey)),
      ),
    );
  }

  Widget _itemSideMenu(IconData icon, String text) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 7),
      child: Row(
        children: [
          Icon(icon, size: 18, color: Colors.grey),
          SizedBox(width: 10),
          Expanded(child: Text(text, style: TextStyle(color: Colors.grey, fontSize: 14), textAlign: TextAlign.start))
        ],
      ),
    );
  }

  Widget _channelView(Channel channel) {
    return Column(
      children: [
        Container(
          height: 65,
          child: Row(
            children: [
              if (isMobile)
                GestureDetector(
                  onTap: () => _rightController.reverse(),
                  child: Padding(
                    padding: const EdgeInsets.only(left: 20),
                    child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 14),
                  ),
                ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.only(left: 20),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      Row(
                        children: [
                          channel.private
                              ? Icon(Icons.lock, color: Colors.white, size: 14)
                              : Text('\u0023', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600)),
                          SizedBox(width: 4),
                          Expanded(
                            child: Text(
                              channel.name,
                              style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: 5),
                      Row(
                        children: [
                          Icon(
                            Icons.person_outline,
                            color: Color(0xFFC7C8CA),
                            size: 18,
                          ),
                          SizedBox(width: 4),
                          Text('${channel.users.length}', style: TextStyle(color: Color(0xFFC7C8CA))),
                          SizedBox(width: 8),
                          Container(height: 12, width: 1, color: Color(0x80C7C8CA)),
                          SizedBox(width: 8),
                          Expanded(
                            child: Text(channel.topic ?? 'Add a topic',
                                style: TextStyle(color: Colors.white.withOpacity(0.6), fontSize: 12),
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis),
                          ),
                        ],
                      ),
                      SizedBox(height: 8),
                    ],
                  ),
                ),
              ),
              if (isMobile)
                Padding(
                  padding: const EdgeInsets.only(left: 20, right: 20),
                  child: Icon(
                    Icons.search,
                    color: Colors.white.withOpacity(0.6),
                    size: 20,
                  ),
                ),
              Icon(
                Icons.info_outline,
                color: Colors.white.withOpacity(0.6),
                size: 20,
              ),
              if (!isMobile) SizedBox(width: 5),
              if (!isMobile) Text('Details', style: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 13)),
              SizedBox(width: 20),
            ],
          ),
        ),
        Separator(vertical: false),
        Expanded(child: _chatView(channel.chats)),
        ConstrainedBox(
          constraints: BoxConstraints(minHeight: 100),
          child: Container(
            margin: const EdgeInsets.only(left: 20, right: 20, bottom: 25),
            decoration: BoxDecoration(
              color: Color(0xFF232529),
              border: Border.all(color: Color(0xFF565856), width: 1),
              borderRadius: BorderRadius.circular(5),
            ),
            child: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 10),
                  child: TextField(
                    controller: _textController,
                    style: TextStyle(color: Colors.white.withOpacity(0.6), fontSize: 14),
                    maxLines: null,
                    onChanged: (text) => setState(() {}),
                    decoration: InputDecoration(
                      border: InputBorder.none,
                      hintText: 'Message #${channel.name}',
                      hintStyle: TextStyle(color: Colors.white.withOpacity(0.4), fontSize: 14),
                    ),
                  ),
                ),
                Container(
                  height: 30,
                  child: Row(
                    children: [
                      SizedBox(width: 8),
                      Icon(Icons.whatshot, color: Color(0xFFC7C8CA), size: 18),
                      SizedBox(width: 8),
                      Container(width: 1, height: 25, color: Color(0x40C7C8CA)),
                      SizedBox(width: 8),
                      Icon(Icons.format_bold, color: Colors.grey[700], size: 18),
                      SizedBox(width: 8),
                      RotatedBox(quarterTurns: 1, child: Icon(Icons.link, color: Colors.grey[700], size: 18)),
                      SizedBox(width: 8),
                      Icon(Icons.format_list_numbered, color: Colors.grey[700], size: 18),
                      SizedBox(width: 8),
                      Icon(Icons.format_indent_increase, color: Colors.grey[700], size: 18),
                      Expanded(child: SizedBox()),
                      Icon(Icons.mood, color: Colors.grey[300], size: 18),
                      SizedBox(width: 8),
                      Icon(Icons.attach_file, color: Colors.grey[300], size: 18),
                      SizedBox(width: 8),
                      GestureDetector(
                        onTap: _textController.text.isNotEmpty
                            ? () {
                                setState(() {
                                  _workspaces[_currentIndex]
                                      .first
                                      .channels[_workspaces[_currentIndex].first.currentChannel]
                                      .chats
                                      .add(Chat()
                                        ..user = Utils.userLogged
                                        ..text = _textController.text
                                        ..timestamp = DateTime.now().millisecondsSinceEpoch);
                                  _textController.clear();
                                });
                              }
                            : null,
                        child: Container(
                          height: 30,
                          width: 30,
                          decoration: BoxDecoration(
                              color: _textController.text.isNotEmpty ? Color(0xFF007A5A) : Colors.transparent,
                              borderRadius: BorderRadius.circular(4)),
                          child: Center(child: Icon(Icons.send, color: Color(0xFFC7C8CA), size: 16)),
                        ),
                      ),
                      SizedBox(width: 5),
                    ],
                  ),
                ),
                SizedBox(height: 4),
              ],
            ),
          ),
        )
      ],
    );
  }

  Widget _dmsView(DM dm) {
    return Column(
      children: [
        Container(
          height: 65,
          child: Row(
            children: [
              if (isMobile)
                GestureDetector(
                  onTap: () => _rightController.reverse(),
                  child: Padding(
                    padding: const EdgeInsets.only(left: 20),
                    child: Icon(Icons.arrow_back_ios, color: Colors.white, size: 14),
                  ),
                ),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.only(left: 20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      Row(
                        children: [
                          dm.user.online ? Utils.online(false) : Utils.offline(false),
                          SizedBox(width: 4),
                          Expanded(
                            child: Text(
                              dm.user.name,
                              style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ],
                      ),
                      SizedBox(height: 5),
                      if (dm.user.status != null)
                        Row(
                          children: [
                            Text(dm.user.status,
                                style: TextStyle(height: 1.2), maxLines: 1, overflow: TextOverflow.ellipsis),
                            if (dm.user.description != null) SizedBox(width: 6),
                            Expanded(
                              child: Text(
                                dm.user.description ?? '',
                                style: TextStyle(color: Colors.grey, height: 1.2),
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                          ],
                        ),
                      SizedBox(height: dm.user.status != null ? 8 : 20),
                    ],
                  ),
                ),
              ),
              SizedBox(width: 20),
              Icon(Icons.phone, size: 25, color: Colors.grey),
              if (!isMobile) SizedBox(width: 20),
              if (!isMobile) Container(width: 1, height: 30, color: Colors.grey),
              SizedBox(width: 20),
              Icon(
                Icons.info_outline,
                color: Colors.white.withOpacity(0.6),
                size: 20,
              ),
              if (!isMobile) SizedBox(width: 5),
              if (!isMobile) Text('Details', style: TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 13)),
              SizedBox(width: 20),
            ],
          ),
        ),
        Separator(vertical: false),
        Expanded(child: _chatView(dm.chats)),
        ConstrainedBox(
          constraints: BoxConstraints(minHeight: 100),
          child: Container(
            margin: const EdgeInsets.only(left: 20, right: 20, bottom: 25),
            decoration: BoxDecoration(
              color: Color(0xFF232529),
              border: Border.all(color: Color(0xFF565856), width: 1),
              borderRadius: BorderRadius.circular(5),
            ),
            child: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 10),
                  child: TextField(
                    controller: _textController,
                    style: TextStyle(color: Colors.white.withOpacity(0.6), fontSize: 14),
                    maxLines: null,
                    onChanged: (text) => setState(() {}),
                    decoration: InputDecoration(
                      border: InputBorder.none,
                      hintText: 'Message #${dm.user.name} ${dm.user.status ?? ''}',
                      hintStyle: TextStyle(color: Colors.white.withOpacity(0.4), fontSize: 14, height: 1.2),
                    ),
                  ),
                ),
                Container(
                  height: 30,
                  child: Row(
                    children: [
                      SizedBox(width: 8),
                      Icon(Icons.whatshot, color: Color(0xFFC7C8CA), size: 18),
                      SizedBox(width: 8),
                      Container(width: 1, height: 25, color: Color(0x40C7C8CA)),
                      SizedBox(width: 8),
                      Icon(Icons.format_bold, color: Colors.grey[700], size: 18),
                      SizedBox(width: 8),
                      RotatedBox(quarterTurns: 1, child: Icon(Icons.link, color: Colors.grey[700], size: 18)),
                      SizedBox(width: 8),
                      Icon(Icons.format_list_numbered, color: Colors.grey[700], size: 18),
                      SizedBox(width: 8),
                      Icon(Icons.format_indent_increase, color: Colors.grey[700], size: 18),
                      Expanded(child: SizedBox()),
                      Icon(Icons.mood, color: Colors.grey[300], size: 18),
                      SizedBox(width: 8),
                      Icon(Icons.attach_file, color: Colors.grey[300], size: 18),
                      SizedBox(width: 8),
                      GestureDetector(
                        onTap: _textController.text.isNotEmpty
                            ? () {
                                setState(() {
                                  _workspaces[_currentIndex]
                                      .first
                                      .dms[_workspaces[_currentIndex].first.currentDM]
                                      .chats
                                      .add(Chat()
                                        ..user = Utils.userLogged
                                        ..text = _textController.text
                                        ..timestamp = DateTime.now().millisecondsSinceEpoch);
                                  _textController.clear();
                                });
                              }
                            : null,
                        child: Container(
                          height: 30,
                          width: 30,
                          decoration: BoxDecoration(
                              color: _textController.text.isNotEmpty ? Color(0xFF007A5A) : Colors.transparent,
                              borderRadius: BorderRadius.circular(4)),
                          child: Center(child: Icon(Icons.send, color: Color(0xFFC7C8CA), size: 16)),
                        ),
                      ),
                      SizedBox(width: 5),
                    ],
                  ),
                ),
                SizedBox(height: 4),
              ],
            ),
          ),
        )
      ],
    );
  }

  Widget _chatView(List<Chat> chats) {
    chats.sort((first, second) => second.timestamp.compareTo(first.timestamp));
    return ListView.builder(
      reverse: true,
      physics: BouncingScrollPhysics(),
      padding: const EdgeInsets.symmetric(vertical: 10),
      itemBuilder: (context, index) {
        final chat = chats[index];
        var sameUser = index + 1 <= chats.length - 1 && chats[index + 1].user.id == chat.user.id;
        return ChatItem(chat: chat, sameUser: sameUser);
      },
      itemCount: chats.length,
    );
  }

  static Widget _menuSection(IconData icon, String text, GestureTapCallback onTap) {
    return InkWell(
      onTap: onTap,
      child: SizedBox(
        height: 30,
        child: Row(
          children: [
            SizedBox(width: 15),
            Container(
              height: 30,
              child: Icon(icon, color: Color(0xFFC7C8CA), size: 15),
            ),
            SizedBox(width: 10),
            Container(
              child: Text(text, style: TextStyle(color: Color(0xFFC7C8CA), fontSize: 14)),
            ),
          ],
        ),
      ),
    );
  }

  Widget _titleSection(String title, GestureTapCallback toggleTap, bool show, GestureTapCallback addTap) {
    return SizedBox(
      height: 40,
      child: Row(
        children: [
          SizedBox(width: 10),
          GestureDetector(
            onTap: toggleTap,
            child: Container(
              height: 40,
              child: Icon(show ? Icons.arrow_drop_down : Icons.arrow_right, color: Color(0xFFC7C8CA), size: 20),
            ),
          ),
          SizedBox(width: 8),
          Expanded(
            child: Container(
              child: Text(title, style: TextStyle(color: Color(0xFFC7C8CA), fontSize: 14)),
            ),
          ),
          InkWell(
            onTap: addTap,
            child: Icon(Icons.add, color: Color(0xFFC7C8CA), size: 20),
          ),
          SizedBox(width: 15),
        ],
      ),
    );
  }

  _initChannels(bool clear) {
    if (clear) _channels.clear();
    _workspaces[_currentIndex].first.channels.asMap().forEach((index, channel) {
      setState(() => _channels
          .add(ChannelItem(key: GlobalKey<ChannelItemState>(), channel: channel, index: index, clickItem: _chanlItem)));
    });
  }

  _initDMs(bool clear) {
    if (clear) _dms.clear();
    _workspaces[_currentIndex].first.dms.asMap().forEach((index, dm) {
      setState(() => _dms.add(DMUserItem(
          key: GlobalKey<DMUserItemState>(),
          user: dm.user,
          index: index,
          closeItem: _closeItem,
          chatListener: _chatsItem)));
    });
  }
}

class ChatItem extends StatefulWidget {
  final Chat chat;
  final bool sameUser;

  const ChatItem({Key key, this.chat, this.sameUser}) : super(key: key);

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

class _ChatItemState extends State<ChatItem> {
  var _hover = false;
  var text = '';
  var url = '';

  @override
  Widget build(BuildContext context) {
    text = widget.chat.text;
    final matches = Utils.regexImageUrl.allMatches(widget.chat.text);
    if (matches.length > 0) {
      url = widget.chat.text.substring(matches.first.start, matches.first.end);
      text = widget.chat.text.substring(0, matches.first.start);
    } else {
      url = '';
    }
    return MouseRegion(
      onHover: (_) => setState(() => _hover = true),
      onExit: (_) => setState(() => _hover = false),
      child: Stack(
        overflow: Overflow.visible,
        children: [
          Container(
            color: _hover ? Colors.white.withOpacity(0.05) : Colors.transparent,
            child: Padding(
              padding: EdgeInsets.only(left: 20, right: 20, top: widget.sameUser ? 5 : 10, bottom: 5),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  if (!widget.sameUser)
                    Container(
                      width: 40,
                      height: 40,
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(5),
                        child: Image.network(widget.chat.user.avatar, fit: BoxFit.cover),
                      ),
                    ),
                  SizedBox(width: 8 + (widget.sameUser ? 40.0 : 0.0)),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        if (!widget.sameUser)
                          Row(
                            children: [
                              Text(widget.chat.user.name,
                                  style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600)),
                              SizedBox(width: 5),
                              if (widget.chat.user.status != null)
                                Text(widget.chat.user.status,
                                    style: TextStyle(color: Colors.white, height: 1.2),
                                    maxLines: 1,
                                    overflow: TextOverflow.ellipsis),
                            ],
                          ),
                        if (!widget.sameUser) SizedBox(height: 6),
                        if (text.isNotEmpty) Text(text, style: TextStyle(color: Colors.grey[400])),
                        if (url.isNotEmpty)
                          Padding(
                            padding: const EdgeInsets.symmetric(vertical: 5),
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                SizedBox(height: 5),
                                Text(
                                  url.split('/')[url.split('/').length - 1],
                                  style: TextStyle(fontSize: 12, color: Colors.grey[700]),
                                ),
                                SizedBox(height: 5),
                                Container(
                                  constraints: BoxConstraints(maxWidth: 400),
                                  child: ClipRRect(
                                    borderRadius: BorderRadius.circular(5),
                                    child: Image.network(url),
                                  ),
                                ),
                              ],
                            ),
                          )
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
          if (_hover)
            Positioned(
              top: 0,
              right: 20,
              child: Transform(
                transform: Matrix4.identity()..translate(0, -15),
                child: Row(
                  children: [
                    Container(
                      decoration: BoxDecoration(
                        color: Color(0xFF232529),
                        borderRadius: BorderRadius.circular(6),
                        border: Border.all(color: Colors.grey[700], width: 0.5),
                      ),
                      padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                      child: Material(
                        color: Colors.transparent,
                        child: Row(
                          children: [
                            SizedBox(width: 5),
                            InkWell(
                              onTap: () {},
                              onHover: (_) {},
                              child: Tooltip(
                                message: 'Add reaction',
                                verticalOffset: -55,
                                textStyle: TextStyle(fontSize: 12, color: Colors.white),
                                decoration: BoxDecoration(
                                    color: Colors.black,
                                    borderRadius: BorderRadius.circular(8),
                                    border: Border.all(color: Colors.grey[700], width: 0.5)),
                                child: Padding(
                                  padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                                  child: Icon(Icons.insert_emoticon, color: Colors.grey, size: 18),
                                ),
                              ),
                            ),
                            SizedBox(width: 6),
                            InkWell(
                              onTap: () {},
                              onHover: (_) {},
                              child: Tooltip(
                                message: 'Start a thread',
                                verticalOffset: -55,
                                textStyle: TextStyle(fontSize: 12, color: Colors.white),
                                decoration: BoxDecoration(
                                    color: Colors.black,
                                    borderRadius: BorderRadius.circular(8),
                                    border: Border.all(color: Colors.grey[700], width: 0.5)),
                                child: Padding(
                                  padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                                  child: Icon(Icons.chat, color: Colors.grey, size: 18),
                                ),
                              ),
                            ),
                            SizedBox(width: 6),
                            InkWell(
                              onTap: () {},
                              onHover: (_) {},
                              child: Tooltip(
                                message: 'Share a message...',
                                verticalOffset: -55,
                                textStyle: TextStyle(fontSize: 12, color: Colors.white),
                                decoration: BoxDecoration(
                                    color: Colors.black,
                                    borderRadius: BorderRadius.circular(8),
                                    border: Border.all(color: Colors.grey[700], width: 0.5)),
                                child: Padding(
                                  padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                                  child: Icon(Icons.forward, color: Colors.grey, size: 18),
                                ),
                              ),
                            ),
                            SizedBox(width: 6),
                            InkWell(
                              onTap: () {},
                              onHover: (_) {},
                              child: Tooltip(
                                message: 'Save',
                                verticalOffset: -55,
                                textStyle: TextStyle(fontSize: 12, color: Colors.white),
                                decoration: BoxDecoration(
                                    color: Colors.black,
                                    borderRadius: BorderRadius.circular(8),
                                    border: Border.all(color: Colors.grey[700], width: 0.5)),
                                child: Padding(
                                  padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                                  child: Icon(Icons.bookmark_border, color: Colors.grey, size: 18),
                                ),
                              ),
                            ),
                            SizedBox(width: 4),
                            InkWell(
                              onTap: () {},
                              onHover: (_) {},
                              child: Tooltip(
                                message: 'More actions',
                                verticalOffset: -55,
                                textStyle: TextStyle(fontSize: 12, color: Colors.white),
                                decoration: BoxDecoration(
                                    color: Colors.black,
                                    borderRadius: BorderRadius.circular(8),
                                    border: Border.all(color: Colors.grey[700], width: 0.5)),
                                child: Padding(
                                  padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
                                  child: Icon(Icons.more_vert, color: Colors.grey, size: 18),
                                ),
                              ),
                            ),
                            SizedBox(width: 5),
                          ],
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            )
        ],
      ),
    );
  }
}

class ChannelItem extends StatefulWidget {
  final int index;
  final Channel channel;
  final ClickItem clickItem;

  const ChannelItem({Key key, this.index, this.channel, this.clickItem}) : super(key: key);

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

class ChannelItemState extends State<ChannelItem> {
  var _selected = false;

  void selected(bool value) => setState(() => _selected = value);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () => widget.clickItem(widget.index),
      child: Container(
        color: _selected ? Color(0xFF1064A3) : Colors.transparent,
        height: 30,
        child: Row(
          children: [
            SizedBox(width: 20),
            Container(
              height: 30,
              child: Icon(widget.channel.private ? Icons.lock : IconData(0x23, fontFamily: 'Roboto'),
                  color: _selected ? Colors.white : Color(0xAAC7C8CA), size: 15),
            ),
            SizedBox(width: 8),
            Container(
              child: Text(widget.channel.name,
                  style: TextStyle(color: _selected ? Colors.white : Color(0xAAC7C8CA), fontSize: 14)),
            ),
          ],
        ),
      ),
    );
  }
}

class DMUserItem extends StatefulWidget {
  final User user;
  final int index;
  final ClickItem chatListener;
  final ClickItem closeItem;

  const DMUserItem({Key key, this.user, this.index, this.chatListener, this.closeItem}) : super(key: key);

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

class DMUserItemState extends State<DMUserItem> {
  var _selected = false;

  void selected(bool value) => setState(() => _selected = value);

  var _index;

  void index(int index) => setState(() => _index = index);

  var _hover = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onHover: (_) => setState(() => _hover = true),
      onExit: (_) => setState(() => _hover = false),
      child: InkWell(
        onTap: () => widget.chatListener(_index == null ? widget.index : _index),
        child: Container(
          color: _selected ? Color(0xFF1064A3) : Colors.transparent,
          height: 30,
          child: Row(
            children: [
              SizedBox(width: 20),
              widget.user.online ? Utils.online(_selected) : Utils.offline(_selected),
              SizedBox(width: 8),
              Expanded(
                child: Row(
                  children: [
                    Text(widget.user.name,
                        style:
                            TextStyle(color: _selected ? Colors.white : Color(0xAAC7C8CA), fontSize: 14, height: 1.2)),
                    if (Utils.userLogged.id == widget.user.id)
                      Padding(
                        padding: const EdgeInsets.only(left: 4),
                        child: Text('(you)', style: TextStyle(color: Colors.white.withOpacity(0.4))),
                      ),
                    if (widget.user.status != null)
                      Padding(
                        padding: const EdgeInsets.only(left: 4),
                        child: Text(
                          widget.user.status,
                          style: TextStyle(color: Colors.grey[700], height: 1.2),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ),
                  ],
                ),
              ),
              Opacity(
                opacity: _hover ? 1 : 0,
                child: InkWell(
                  onTap: () => widget.closeItem(_index == null ? widget.index : _index),
                  child: Icon(Icons.clear, color: Color(0xFFC7C8CA), size: 18),
                ),
              ),
              SizedBox(width: 15),
            ],
          ),
        ),
      ),
    );
  }
}

class WorkspaceItem extends StatefulWidget {
  final int index;
  final bool selected;
  final Workspace workspace;
  final ClickItem clickItem;

  const WorkspaceItem({Key key, this.index, this.selected = false, this.workspace, this.clickItem}) : super(key: key);

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

class WorkspaceItemState extends State<WorkspaceItem> {
  var _selected = false;

  void selected(bool value) => setState(() => _selected = value);

  var _hover = false;

  @override
  void initState() {
    super.initState();
    if (mounted) Future.microtask(() => setState(() => _selected = widget.selected));
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => widget.clickItem(widget.index),
      child: AspectRatio(
        aspectRatio: 1,
        child: Stack(
          children: [
            MouseRegion(
              onHover: (_) => setState(() => _hover = true),
              onExit: (_) => setState(() => _hover = false),
              child: Container(
                padding: const EdgeInsets.all(3),
                margin: const EdgeInsets.all(8),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  border: Border.all(
                      color: _selected ? Colors.white : _hover ? Colors.white.withOpacity(0.2) : Colors.transparent,
                      width: 3),
                ),
                child: widget.workspace.logo == null
                    ? Container(
                        decoration: BoxDecoration(
                          color: Colors.grey,
                          borderRadius: BorderRadius.circular(5),
                        ),
                        child: Center(
                          child: Text(
                            widget.workspace.name.substring(0, 1),
                            style: TextStyle(
                              fontSize: 18,
                              color: Colors.black,
                              fontWeight: FontWeight.w600,
                            ),
                          ),
                        ),
                      )
                    : ClipRRect(
                        borderRadius: BorderRadius.circular(5),
                        child: Image.network(widget.workspace.logo),
                      ),
              ),
            ),
            if (!_selected)
              Container(
                padding: const EdgeInsets.all(8),
                child: Align(
                  alignment: Alignment.topRight,
                  child: ClipOval(
                    child: Container(
                      width: 12,
                      height: 12,
                      color: Color(0xFE1D2229),
                      padding: const EdgeInsets.all(3),
                      child: ClipOval(
                        child: Container(
                          width: 12,
                          height: 12,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }
}

class Separator extends StatelessWidget {
  final bool vertical;

  const Separator({Key key, @required this.vertical}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;
    return Container(
      width: vertical ? 1 : size.width,
      height: vertical ? size.height : 1,
      color: Color(0xFF34393F),
    );
  }
}

typedef ClickItem(int index);
typedef ChatListener<T>(T item);

class Utils {
  static final userLogged = User()
    ..id = 1
    ..name = 'Mariano Zorrilla'
    ..status = '💙'
    ..description = 'I clone apps!'
    ..avatar = 'https://pbs.twimg.com/profile_images/1222274976415281153/TVSI4DIx_400x400.jpg'
    ..online = true;

  static final simon = User()
    ..id = 2
    ..name = 'Simon'
    ..status = '🇬🇧'
    ..description = 'Admin https://github.com/slightfoot'
    ..avatar = 'https://pbs.twimg.com/profile_images/1017532253394624513/LgFqlJ4U_400x400.jpg'
    ..online = true;

  static final scott = User()
    ..id = 3
    ..name = 'Scott'
    ..avatar = 'https://pbs.twimg.com/profile_images/1050017782811713536/6tKkzfsI_400x400.jpg'
    ..online = true;

  static final nash = User()
    ..id = 4
    ..name = 'Nash Ramdial'
    ..avatar = 'https://pbs.twimg.com/profile_images/1180232818779021312/xlZcHrlQ_400x400.jpg'
    ..online = false;

  static final chris = User()
    ..id = 5
    ..name = 'Chris Sells'
    ..avatar = 'https://pbs.twimg.com/profile_images/1660905119/vikingme128x128_400x400.jpg'
    ..online = false;

  static final nilay = User()
    ..id = 6
    ..name = 'Nilay'
    ..avatar = 'https://pbs.twimg.com/profile_images/1132357550379134976/tRQLk7Je_400x400.jpg'
    ..online = false;

  static final tim = User()
    ..id = 6
    ..name = 'Tim Sneath'
    ..avatar = 'https://pbs.twimg.com/profile_images/653618067084218368/XlQA-oRl.jpg'
    ..online = true;

  static final ariel = User()
    ..id = 7
    ..name = 'Ariel Viera'
    ..status = '🇦🇷'
    ..avatar = 'https://pbs.twimg.com/profile_images/1204953056686809089/2wwFUZNZ_400x400.jpg'
    ..online = true;

  static final martin = User()
    ..id = 8
    ..name = 'Martin Aguinis'
    ..status = '🇦🇷'
    ..avatar = 'https://pbs.twimg.com/profile_images/1139005628846878721/lSg5Loq4_400x400.jpg'
    ..online = true;

  static final List<User> allUsers = [userLogged, chris, martin, ariel, nilay, simon, scott, tim, nash];

  static IconData get at => IconData(0x40, fontFamily: 'Roboto');

  static IconData get hash => IconData(0x23, fontFamily: 'Roboto');

  static Text online(bool selected) =>
      Text('\u25CF', style: TextStyle(color: selected ? Colors.grey[300] : Color(0xFF93E865), fontSize: 20));

  static Text offline(bool selected) =>
      Text('\u25CB', style: TextStyle(color: selected ? Colors.grey[300] : Colors.grey, fontSize: 20));

  static final RegExp regexpEmoji =
      RegExp(r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])');
  static final RegExp regexAlphaNum = RegExp(r'[a-zA-Z0-9]');
  static final RegExp regexImageUrl = RegExp(r'(https?:\/\/.*\.(?:png|jpg|gif))');

  static bool singleEmoji(Chat chat) {
    if (Utils.regexAlphaNum.hasMatch(chat.text)) {
      return false;
    } else {
      return Utils.regexpEmoji.hasMatch(chat.text);
    }
  }
}

class Tuple<F, S> {
  F first;
  S second;

  Tuple(this.first, this.second);
}

class Workspace {
  String name;
  String url;
  String logo;
  User user;
  List<User> users;
  List<Channel> channels;
  List<DM> dms;
  int currentChannel = 0;
  int currentDM = -1;
}

class User {
  int id;
  String name;
  String status;
  String description;
  String avatar;
  bool online;
}

class Channel extends BaseChat {
  String name;
  String topic;
  List<User> users;
  bool private;
}

class DM extends BaseChat {
  User user;
}

class Chat {
  int timestamp;
  String text;
  User user;
}

abstract class BaseChat {
  List<Chat> chats;
}

              
            
!
999px

Console