Flutter3WinChat客户端聊天|flutter3.x+getx桌面端仿微信EXE实例。

null

该项目前后花了差不多大半个月时间,旨在通过这个项目探索Flutter桌面端开发。

null

技术框架

  • 编辑器:vscode
  • 窗口管理:bitsdojo_window: ^0.1.6
  • 托盘图标:system_tray: ^2.0.3
  • 路由/状态管理:get: ^4.6.6
  • 本地存储:get_storage: ^2.1.1
  • 图片预览插件:photo_view: ^0.14.0
  • 网址预览:url_launcher: ^6.2.4
  • 视频组件:media_kit: ^1.1.10+1
  • 文件选择器:file_picker: ^6.1.1

null

整个项目使用到了上面的一些技术,窗口管理采用的bitsdojo_window插件,不过window_manager这个窗口管理插件也不错,不过相对重量级一些。

https://pub-web.flutter-io.cn/packages/bitsdojo_window
https://pub-web.flutter-io.cn/packages/window_manager

null

项目结构

null

使用flutter create flutter_app创建一个新项目。
使用flutter run -d windows来运行到window桌面。

null

null

null

null

null

null

null

null

null

null

null

null

null

null

null

Flutter路由/状态管理

使用Getx来作为路由及状态管理。

null

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'FLUTTER3 WINCHAT',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: FStyle.primaryColor,
        useMaterial3: true,
      ),
      home: const Layout(),
      // 初始路由
      initialRoute: Utils.isLogin() ? '/index' :'/login',
      // 路由页面
      getPages: routes,
    );
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';

// 引入工具类
import '../utils/index.dart';

/* 引入路由页面 */
import '../views/auth/login.dart';
import '../views/auth/register.dart';
// 首页
import '../views/index/index.dart';
// 通讯录
import '../views/contact/index.dart';
import '../views/contact/addfriends.dart';
import '../views/contact/newfriends.dart';
import '../views/contact/uinfo.dart';
// 收藏
import '../views/favor/index.dart';
// 我的
import '../views/my/index.dart';
import '../views/my/setting.dart';
import '../views/my/recharge.dart';
import '../views/my/wallet.dart';
// 朋友圈
import '../views/fzone/index.dart';
import '../views/fzone/publish.dart';
// 短视频
import '../views/fvideo/index.dart';
// 聊天
import '../views/chat/group-chat/chat.dart';

// 路由地址集合
final Map<String, Widget> routeMap = {
  '/index': const Index(),
  '/contact': const Contact(),
  '/addfriends': const AddFriends(),
  '/newfriends': const NewFriends(),
  '/uinfo': const Uinfo(),
  '/favor': const Favor(),
  '/my': const My(),
  '/setting': const Setting(),
  '/recharge': const Recharge(),
  '/wallet': const Wallet(),
  '/fzone': const Fzone(),
  '/publish': const PublishFzone(),
  '/fvideo': const Fvideo(),
  '/chat': const Chat(),
};

final List<GetPage> patchRoute = routeMap.entries.map((e) => GetPage(
  name: e.key, // 路由名称
  page: () => e.value, // 路由页面
  transition: Transition.noTransition, // 跳转路由动画
  middlewares: [AuthMiddleware()], // 路由中间件
)).toList();

final List<GetPage> routes = [
  GetPage(name: '/login', page: () => const Login()),
  GetPage(name: '/register', page: () => const Register()),
  ...patchRoute,
];

Flutter布局模板

null

null

class Layout extends StatefulWidget {
  const Layout({
    super.key,
    this.activitybar = const Activitybar(),
    this.sidebar,
    this.workbench,
    this.showSidebar = true,
  });

  final Widget? activitybar; // 左侧操作栏
  final Widget? sidebar; // 侧边栏
  final Widget? workbench; // 右侧工作面板
  final bool showSidebar; // 是否显示侧边栏

  @override
  State<Layout> createState() => _LayoutState();
}
return Scaffold(
  backgroundColor: Colors.grey[100],
  body: Flex(
    direction: Axis.horizontal,
    children: [
      // 左侧操作栏
      MoveWindow(
        child: widget.activitybar,
        onDoubleTap: () => {},
      ),
      // 侧边栏
      Visibility(
        visible: widget.showSidebar,
        child: SizedBox(
          width: 270.0,
          child: Container(
            decoration: const BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [
                  Color(0xFFEEEBE7), Color(0xFFEEEEEE)
                ]
              ),
            ),
            child: widget.sidebar,
          ),
        ),
      ),
      // 主体容器
      Expanded(
        child: Column(
          children: [
            WindowTitleBarBox(
              child: Row(
                children: [
                  Expanded(
                    child: MoveWindow(),
                  ),
                  // 右上角操作按钮组
                  Winbtn(
                    leading: Row(
                      children: [
                        IconButton(onPressed: () {}, icon: const Icon(Icons.auto_fix_high), iconSize: 14.0,),
                        IconButton(
                          onPressed: () {
                            setState(() {
                              winTopMost = !winTopMost;
                            });
                          },
                          tooltip: winTopMost ? '取消置顶' : '置顶',
                          icon: const Icon(Icons.push_pin_outlined),
                          iconSize: 14.0,
                          highlightColor: Colors.transparent, // 点击水波纹颜色
                          isSelected: winTopMost ? true : false, // 是否选中
                          style: ButtonStyle(
                            visualDensity: VisualDensity.compact,
                            backgroundColor: MaterialStateProperty.all(winTopMost ? Colors.grey[300] : Colors.transparent),
                            shape: MaterialStatePropertyAll(
                              RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0))
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
            // 右侧工作面板
            Expanded(
              child: Container(
                child: widget.workbench,
              ),
            ),
          ],
        ),
      ),
    ],
  ),
);

Flutter朋友圈

null

null

@override
Widget build(BuildContext context) {
  return Layout(
    showSidebar: false,
    workbench: CustomScrollView(
      slivers: [
        SliverAppBar(
          backgroundColor: const Color(0xFF224E7F),
          foregroundColor: Colors.white,
          pinned: true,
          elevation: 0.0,
          expandedHeight: 200.0,
          leading: IconButton(icon: const Icon(Icons.arrow_back,), onPressed: () {Navigator.pop(context);}),
          flexibleSpace: FlexibleSpaceBar(
            title: Row(
              children: <Widget>[
                ClipOval(child: Image.asset('assets/images/avatar/uimg1.jpg',height: 36.0,width: 36.0,fit: BoxFit.fill)),
                const SizedBox(width: 10.0),
                const Text('Andy', style: TextStyle(fontSize: 14.0)),
              ],
            ),
            titlePadding: const EdgeInsets.fromLTRB(55, 10, 10, 10),
            background: InkWell(
              child: Image.asset('assets/images/cover.jpg', fit: BoxFit.cover),
              onTap: () {changePhotoAlbum(context);},
            ),
          ),
          actions: <Widget>[
            IconButton(icon: const Icon(Icons.favorite_border, size: 18,), onPressed: () {}),
            IconButton(icon: const Icon(Icons.share, size: 18,), onPressed: () {}),
            IconButton(icon: const Icon(Icons.add_a_photo, size: 18,), onPressed: () {Get.toNamed('/publish');}),
            const SizedBox(width: 10.0,),
          ],
        ),
        SliverToBoxAdapter(
          child: UnconstrainedBox(
            child: Container(
              width: MediaQuery.of(context).size.height * 3 / 4,
              decoration: const BoxDecoration(
                color: Colors.white,
              ),
              child: Column(
                children: uzoneList.map((item) {
                  return Container(
                    padding: const EdgeInsets.all(15.0),
                    decoration: const BoxDecoration(
                      border: Border(bottom: BorderSide(color: Color(0xFFEEEEEE), width: .5)),
                    ),
                    child: Row(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        Image.asset(item['avatar'],height: 35.0,width: 35.0,fit: BoxFit.cover),
                        const SizedBox(width: 10.0),
                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: <Widget>[
                              Text(item['author'], style: TextStyle(color: Colors.indigo[400])),
                              const SizedBox(height: 2.0),
                              Text(item['content'], style: const TextStyle(color: Colors.black87, fontSize: 15.0)),
                              const SizedBox(height: 10.0),
                              GroupZone(images: item['images']),
                              const SizedBox(height: 10.0),
                              Row(
                                children: <Widget>[
                                  Expanded(child: Text(item['time'], style: const TextStyle(color: Colors.grey, fontSize: 12.0)),),
                                  FStyle.iconfont(0xe653, color: Colors.black54, size: 16.0,),
                                ],
                              )
                            ],
                          ),
                        ),
                      ],
                    ),
                  );
                }).toList(),
              ),
            ),
          ),
        ),
      ],
    ),
  );
}

OK,综上就是flutter3+getx开发桌面端聊天项目的一些分享。

作者:xiaoyan2015
链接:https://juejin.cn/post/7341533476674207755
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。