<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      flutter3+dart3聊天室|Flutter3跨平臺(tái)仿微信App語音聊天/朋友圈

      全新研發(fā)flutter3+dart3+photo_view跨多端仿微信App界面聊天Flutter3-Chat

      flutter3-chat基于最新跨全平臺(tái)技術(shù)flutter3+dart3+material-design+shared_preferences+easy_refresh構(gòu)建的仿微信APP界面聊天實(shí)例項(xiàng)目。實(shí)現(xiàn)發(fā)送圖文表情消息/gif大圖、長按仿微信語音操作面板、圖片預(yù)覽、紅包及朋友圈等功能。

      flutter3-winseek客戶端AI實(shí)例|Flutter3.32+DeepSeek流式ai對(duì)話模板Exe

      flutter3-deepseek流式AI模板|Flutter3.27+Dio+DeepSeeek聊天ai助手

      技術(shù)架構(gòu)

      • 編輯器:Vscode
      • 框架技術(shù):Flutter3.16.5+Dart3.2.3
      • UI組件庫:material-design3
      • 彈窗組件:showDialog/SimpleDialog/showModalBottomSheet/AlertDialog
      • 圖片預(yù)覽:photo_view^0.14.0
      • 本地緩存:shared_preferences^2.2.2
      • 下拉刷新:easy_refresh^3.3.4
      • toast提示:toast^0.3.0
      • 網(wǎng)址預(yù)覽組件:url_launcher^6.2.4

      Flutter3.x開發(fā)跨平臺(tái)項(xiàng)目,性能有了大幅度提升,官方支持編譯到android/ios/macos/windows/linux/web等多平臺(tái),未來可期!

      項(xiàng)目構(gòu)建目錄

      通過 flutter create app_project 命令即可快速創(chuàng)建一個(gè)跨平臺(tái)初始化項(xiàng)目。

      通過命令創(chuàng)建項(xiàng)目后,項(xiàng)目結(jié)構(gòu)就如上圖所示。

      需要注意:flutter項(xiàng)目基于dart語音開發(fā),需要首先配置Dart SDK和Flutter SDK開發(fā)環(huán)境,大家可以去官網(wǎng)查看配置文檔。

      https://flutter.dev/

      https://flutter.cn/

      https://pub.flutter-io.cn/

      https://www.dartcn.com/

      另外使用VScode編輯器開發(fā)項(xiàng)目,可自行安裝Flutter / Dart擴(kuò)展插件。

      目前flutter3-chat聊天app已經(jīng)正式同步到我的原創(chuàng)作品集。

      原創(chuàng)flutter3+getx跨平臺(tái)仿微信app聊天|朋友圈

      由于flutter3支持編譯到windows,大家可以開發(fā)初期在windows上面調(diào)試,后期release apk到手機(jī)上。

      通過如下命令即可運(yùn)行到windows平臺(tái)

      flutter run -d windows 

      運(yùn)行后默認(rèn)窗口大小為1280x720,可以修改windows/runner/main.cpp文件里面的窗口尺寸。

      同樣,可以通過 flutter run -d chrome 命令運(yùn)行到web上預(yù)覽。

      假如在沒有真機(jī)的情況下,我們可以選擇模擬器調(diào)試。目前市面上有很多類型模擬器,他們使用adb連接時(shí)都會(huì)有不同的默認(rèn)端口,下面列出了一些常用的模擬器及端口號(hào)。通過adb connect連接上指定模擬器之后,執(zhí)行flutter run命令即可運(yùn)行項(xiàng)目到模擬器上面。

      flutter3實(shí)現(xiàn)圓角文本框及漸變按鈕

      Container(
        height: 40.0,
        margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 30.0),
        decoration: BoxDecoration(
          color: Colors.white,
          border: Border.all(color: const Color(0xffdddddd)),
          borderRadius: BorderRadius.circular(15.0),
        ),
        child: Row(
          children: [
            Expanded(
              child: TextField(
                keyboardType: TextInputType.phone,
                controller: fieldController,
                decoration: InputDecoration(
                  hintText: '輸入手機(jī)號(hào)',
                  suffixIcon: Visibility(
                    visible: authObj['tel'].isNotEmpty,
                    child: InkWell(
                      hoverColor: Colors.transparent,
                      highlightColor: Colors.transparent,
                      splashColor: Colors.transparent,
                      onTap: handleClear,
                      child: const Icon(Icons.clear, size: 16.0,),
                    )
                  ),
                  contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 12.0),
                  border: const OutlineInputBorder(borderSide: BorderSide.none),
                ),
                onChanged: (value) {
                  setState(() {
                    authObj['tel'] = value;
                  });
                },
              ),
            )
          ],
        ),
      ),

      按鈕漸變則是通過Container組件的decotaion里面的gradient屬性設(shè)置漸變效果。

      Container(
        margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(15.0),
          // 自定義按鈕漸變色
          gradient: const LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [
              Color(0xFF0091EA), Color(0xFF07C160)
            ],
          )
        ),
        child: SizedBox(
          width: double.infinity,
          height: 45.0,
          child: FilledButton(
            style: ButtonStyle(
              backgroundColor: MaterialStateProperty.all(Colors.transparent),
              shadowColor: MaterialStateProperty.all(Colors.transparent),
              shape: MaterialStatePropertyAll(
                RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0))
              )
            ),
            onPressed: handleSubmit,
            child: const Text('登錄', style: TextStyle(fontSize: 18.0),),
          ),
        )
      ),

      flutter實(shí)現(xiàn)60s倒計(jì)時(shí)發(fā)送驗(yàn)證碼功能。

      Timer? timer;
      String vcodeText = '獲取驗(yàn)證碼';
      bool disabled = false;
      int time = 60;
      
      // 60s倒計(jì)時(shí)
      void handleVcode() {
        if(authObj['tel'] == '') {
          snackbar('手機(jī)號(hào)不能為空');
        }else if(!Utils.checkTel(authObj['tel'])) {
          snackbar('手機(jī)號(hào)格式不正確');
        }else {
          setState(() {
            disabled = true;
          });
          startTimer();
        }
      }
      startTimer() {
        timer = Timer.periodic(const Duration(seconds: 1), (timer) {
          setState(() {
            if(time > 0) {
              vcodeText = '獲取驗(yàn)證碼(${time--})';
            }else {
              vcodeText = '獲取驗(yàn)證碼';
              time = 60;
              disabled = false;
              timer.cancel();
            }
          });
        });
        snackbar('短信驗(yàn)證碼已發(fā)送,請(qǐng)注意查收', color: Colors.green);
      }

      Flutter3沉浸式漸變狀態(tài)導(dǎo)航欄

      要實(shí)現(xiàn)如上圖漸變AppBar也非常簡(jiǎn)單,只需要配置AppBar提供的可伸縮靈活區(qū)域?qū)傩?nbsp;flexibleSpace 配合gradient即可快速實(shí)現(xiàn)漸變導(dǎo)航欄。

      AppBar(
        title: Text('Flutter3-Chat'),
        flexibleSpace: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
              colors: [
                Color(0xFF0091EA), Color(0xFF07C160)
              ],
            )
          ),
        )
      ),

      Flutter3字體圖標(biāo)/自定義badge

      flutter內(nèi)置了豐富的字體圖標(biāo),通過圖標(biāo)組件 Icon(Icons.add) 引入即可使用。

      https://api.flutter-io.cn/flutter/material/Icons-class.html

      另外還支持通過自定義IconData方式自定義圖標(biāo),如使用阿里iconfont圖表庫圖標(biāo)。

      Icon(IconData(0xe666, fontFamily: 'iconfont'), size: 18.0) 

      把下載的字體文件放到assets目錄,

      pubspec.yaml中引入字體文件。

      class FStyle {
        // 自定義iconfont圖標(biāo)
        static iconfont(int codePoint, {double size = 16.0, Color? color}) {
          return Icon(
            IconData(codePoint, fontFamily: 'iconfont', matchTextDirection: true),
            size: size,
            color: color,
          );
        }
      
        // 自定義Badge紅點(diǎn)
        static badge(int count, {
          Color color = Colors.redAccent,
          bool isdot = false,
          double height = 18.0,
          double width = 18.0
        }) {
          final num = count > 99 ? '99+' : count;
          return Container(
            alignment: Alignment.center,
            height: isdot ? height / 2 : height,
            width: isdot ? width / 2 : width,
            decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.00)),
            child: isdot ? null : Text('$num', style: const TextStyle(color: Colors.white, fontSize: 12.0)),
          );
        }
      }

      FStyle.badge(23)
      FStyle.badge(2, color: Colors.pink, height: 10.0, width: 10.0)
      FStyle.badge(0, isdot: true)

      Flutter仿微信PopupMenu下拉菜單/下拉刷新

      通過flutter提供的PopupMenuButton組件實(shí)現(xiàn)下拉菜單功能。

      PopupMenuButton(
        icon: FStyle.iconfont(0xe62d, size: 17.0),
        offset: const Offset(0, 50.0),
        tooltip: '',
        color: const Color(0xFF353535),
        itemBuilder: (BuildContext context) {
          return <PopupMenuItem>[
            popupMenuItem(0xe666, '發(fā)起群聊', 0),
            popupMenuItem(0xe75c, '添加朋友', 1),
            popupMenuItem(0xe603, '掃一掃', 2),
            popupMenuItem(0xe6ab, '收付款', 3),
          ];
        },
        onSelected: (value) {
          switch(value) {
            case 0:
              print('發(fā)起群聊');
              break;
            case 1:
              Navigator.pushNamed(context, '/addfriends');
              break;
            case 2:
              print('掃一掃');
              break;
            case 3:
              print('收付款');
              break;
          }
        },
      )
      // 下拉菜單項(xiàng)
      static popupMenuItem(int codePoint, String title, value) {
        return PopupMenuItem(
          value: value,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              const SizedBox(width: 10.0,),
              FStyle.iconfont(codePoint, size: 21.0, color: Colors.white),
              const SizedBox(width: 10.0,),
              Text(title, style: const TextStyle(fontSize: 16.0, color: Colors.white),),
            ],
          ),
        );
      }

      如上圖:下拉刷新、上拉加載更多是通過 easy_refresh 組件實(shí)現(xiàn)功能。

      EasyRefresh(
        // 下拉加載提示
        header: const ClassicHeader(
          // showMessage: false,
        ),
        // 加載更多提示
        footer: ClassicFooter(),
        // 下拉刷新邏輯
        onRefresh: () async {
          // ...下拉邏輯
          await Future.delayed(const Duration(seconds: 2));
        },
        // 上拉加載邏輯
        onLoad: () async {
          // ...
        },
        child: ListView.builder(
          itemCount: chatList.length,
          itemBuilder: (context, index) {
            return Ink(
              // ...
            );
          },
        ),
      )

      如上圖:彈窗功能均是自定義AlertDialog實(shí)現(xiàn)效果。通過無限制容器UnconstrainedBox配合SizedBox組件實(shí)現(xiàn)自定義窗口大小。

      // 關(guān)于彈窗
      void aboutAlertDialog(BuildContext context) {
        showDialog(
          context: context,
          builder: (context) {
            return UnconstrainedBox(
              constrainedAxis: Axis.vertical,
              child: SizedBox(
                width: 320.0,
                child: AlertDialog(
                  contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
                  backgroundColor: Colors.white,
                  surfaceTintColor: Colors.white,
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
                  content: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 10.0),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Image.asset('assets/images/logo.png', width: 90.0, height: 90.0, fit: BoxFit.cover,),
                        const SizedBox(height: 10.0),
                        const Text('Flutter3-WChat', style: TextStyle(color: Color(0xFF0091EA), fontSize: 22.0),),
                        const SizedBox(height: 5.0),
                        const Text('基于flutter3+dart3開發(fā)跨平臺(tái)仿微信App聊天實(shí)例。', style: TextStyle(color: Colors.black45),),
                        const SizedBox(height: 20.0),
                        Text('?2024/01 Andy   Q: 282310962', style: TextStyle(color: Colors.grey[400], fontSize: 12.0),),
                      ],
                    ),
                  ),
                ),
              ),
            );
          }
        );
      }
      
      // 二維碼名片彈窗
      void qrcodeAlertDialog(BuildContext context) {
        showDialog(
          context: context,
          builder: (context) {
            return UnconstrainedBox(
              constrainedAxis: Axis.vertical,
              child: SizedBox(
                width: 320.0,
                child: AlertDialog(
                  contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
                  backgroundColor: const Color(0xFF07C160),
                  surfaceTintColor: const Color(0xFF07C160),
                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)),
                  content: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 10.0),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Image.asset('assets/images/qrcode.png', width: 250.0, fit: BoxFit.cover,),
                        const SizedBox(height: 15.0),
                        const Text('掃一掃,加我公眾號(hào)', style: TextStyle(color: Colors.white60, fontSize: 14.0,),),
                      ],
                    ),
                  ),
                ),
              ),
            );
          }
        );
      }
      
      // 退出登錄彈窗
      void logoutAlertDialog(BuildContext context) {
        showDialog(
          context: context,
          builder: (context) {
            return AlertDialog(
              content: const Text('確定要退出登錄嗎?', style: TextStyle(fontSize: 16.0),),
              backgroundColor: Colors.white,
              surfaceTintColor: Colors.white,
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
              elevation: 2.0,
              actionsPadding: const EdgeInsets.all(15.0),
              actions: [
                TextButton(
                  onPressed: () {Navigator.of(context).pop();},
                  child: const Text('取消', style: TextStyle(color: Colors.black54),)
                ),
                TextButton(
                  onPressed: handleLogout,
                  child: const Text('退出登錄', style: TextStyle(color: Colors.red),)
                ),
              ],
            );
          }
        );
      }

      flutter實(shí)現(xiàn)微信朋友圈九宮格

      GroupZone(images: item['images']),
      
      GroupZone(
        images: uploadList,
        album: true,
        onChoose: () async {
          Toast.show('選擇手機(jī)相冊(cè)圖片', duration: 2, gravity: 1);
        },
      ),

      // 創(chuàng)建可點(diǎn)擊預(yù)覽圖片
      createImage(BuildContext context, String img, int key) {
        return GestureDetector(
          child: Hero(
            tag: img, // 放大縮小動(dòng)畫效果標(biāo)識(shí)
            child: img == '+' ? 
            Container(color: Colors.transparent, child: const Icon(Icons.add, size: 30.0, color: Colors.black45),)
            :
            Image.asset(
              img,
              width: width,
              fit: BoxFit.contain,
            ),
          ),
          onTap: () {
            // 選擇圖片
            if(img == '+') {
              onChoose!();
            }else {
              Navigator.of(context).push(FadeRoute(route: ImageViewer(
                images: album ? imgList!.sublist(0, imgList!.length - 1) : imgList,
                index: key,
              )));
            }
          },
        );
      }

      使用photo_view插件實(shí)現(xiàn)預(yù)覽大圖功能,支持預(yù)覽單張及多張大圖。

      import 'package:flutter/material.dart';
      import 'package:photo_view/photo_view.dart';
      import 'package:photo_view/photo_view_gallery.dart';
      
      class ImageViewer extends StatefulWidget {
        const ImageViewer({
          super.key,
          this.images,
          this.index = 0,
        });
      
        final List? images; // 預(yù)覽圖列表
        final int index; // 當(dāng)前預(yù)覽圖索引
      
        @override
        State<ImageViewer> createState() => _ImageViewerState();
      }
      
      class _ImageViewerState extends State<ImageViewer> {
        int currentIndex = 0;
      
        @override
        void initState() {
          super.initState();
          currentIndex = widget.index;
        }
      
        @override
        Widget build(BuildContext context) {
          var imgCount = widget.images?.length;
      
          return Scaffold(
            body: Stack(
              children: [
                Positioned(
                  top: 0,
                  left: 0,
                  bottom: 0,
                  right: 0,
                  child: GestureDetector(
                    child: imgCount == 1 ? PhotoView(
                      imageProvider: AssetImage(widget.images![0]),
                      backgroundDecoration: const BoxDecoration(
                        color: Colors.black,
                      ),
                      minScale: PhotoViewComputedScale.contained,
                      maxScale: PhotoViewComputedScale.covered * 2,
                      heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
                      enableRotation: true,
                    )
                    :
                    PhotoViewGallery.builder(
                      itemCount: widget.images?.length,
                      builder: (context, index) {
                        return PhotoViewGalleryPageOptions(
                          imageProvider: AssetImage(widget.images![index]),
                          minScale: PhotoViewComputedScale.contained,
                          maxScale: PhotoViewComputedScale.covered * 2,
                          heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
                        );
                      },
                      scrollPhysics: const BouncingScrollPhysics(),
                      backgroundDecoration: const BoxDecoration(
                        color: Colors.black,
                      ),
                      pageController: PageController(initialPage: widget.index),
                      enableRotation: true,
                      onPageChanged: (index) {
                        setState(() {
                          currentIndex = index;
                        });
                      },
                    ),
                    onTap: () {
                      Navigator.of(context).pop();
                    },
                  ),
                ),
                // 圖片索引index
                Positioned(
                  top: MediaQuery.of(context).padding.top + 15,
                  width: MediaQuery.of(context).size.width,
                  child: Center(
                    child: Visibility(
                      visible: imgCount! > 1 ? true : false,
                      child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white)),
                    )
                  ),
                ),
              ],
            ),
          );
        }
      }

      flutter3聊天模塊

      文本框TextField設(shè)置maxLines: null即可實(shí)現(xiàn)多行文本輸入,支持圖文emoj混排,網(wǎng)址連接識(shí)別等功能。

      // 輸入框
      Offstage(
        offstage: voiceBtnEnable,
        child: TextField(
          decoration: const InputDecoration(
            isDense: true,
            hoverColor: Colors.transparent,
            contentPadding: EdgeInsets.all(8.0),
            border: OutlineInputBorder(borderSide: BorderSide.none),
          ),
          style: const TextStyle(fontSize: 16.0,),
          maxLines: null,
          controller: editorController,
          focusNode: editorFocusNode,
          cursorColor: const Color(0xFF07C160),
          onChanged: (value) {},
        ),
      ),

       支持仿微信語音按住說話,左滑取消發(fā)送、右滑轉(zhuǎn)換語音功能。

      // 語音
      Offstage(
        offstage: !voiceBtnEnable,
        child: GestureDetector(
          child: Container(
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(5),
            ),
            alignment: Alignment.center,
            height: 40.0,
            width: double.infinity,
            child: Text(voiceTypeMap[voiceType], style: const TextStyle(fontSize: 15.0),),
          ),
          onPanStart: (details) {
            setState(() {
              voiceType = 1;
              voicePanelEnable = true;
            });
          },
          onPanUpdate: (details) {
            Offset pos = details.globalPosition;
            double swipeY = MediaQuery.of(context).size.height - 120;
            double swipeX = MediaQuery.of(context).size.width / 2 + 50;
            setState(() {
              if(pos.dy >= swipeY) {
                voiceType = 1; // 松開發(fā)送
              }else if (pos.dy < swipeY && pos.dx < swipeX) {
                voiceType = 2; // 左滑松開取消
              }else if (pos.dy < swipeY && pos.dx >= swipeX) {
                voiceType = 3; // 右滑語音轉(zhuǎn)文字
              }
            });
          },
          onPanEnd: (details) {
            // print('停止錄音');
            setState(() {
              switch(voiceType) {
                case 1:
                  Toast.show('發(fā)送錄音文件', duration: 1, gravity: 1);
                  voicePanelEnable = false;
                  break;
                case 2:
                  Toast.show('取消發(fā)送', duration: 1, gravity: 1);
                  voicePanelEnable = false;
                  break;
                case 3:
                  Toast.show('語音轉(zhuǎn)文字', duration: 1, gravity: 1);
                  voicePanelEnable = true;
                  voiceToTransfer = true;
                  break;
              }
              voiceType = 0;
            });
          },
        ),
      ),

      按住錄音顯示面板

      // 錄音主體(按住說話/松開取消/語音轉(zhuǎn)文本)
      Visibility(
        visible: voicePanelEnable,
        child: Material(
          color: const Color(0xDD1B1B1B),
          child: Stack(
            children: [
              // 取消發(fā)送+語音轉(zhuǎn)文字
              Positioned(
                bottom: 120,
                left: 30,
                right: 30,
                child: Visibility(
                  visible: !voiceToTransfer,
                  child: Column(
                    children: [
                      // 語音動(dòng)畫層
                      Stack(
                        children: [
                          Container(
                            height: 70.0,
                            margin: const EdgeInsets.symmetric(horizontal: 50.0),
                            decoration: BoxDecoration(
                              color: Colors.white,
                              borderRadius: BorderRadius.circular(15.0),
                            ),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Image.asset('assets/images/voice_record.gif', height: 30.0,)
                              ],
                            ),
                          ),
                          Positioned(
                            right: (MediaQuery.of(context).size.width - 60) / 2,
                            bottom: 1,
                            child: RotatedBox(
                              quarterTurns: 0,
                              child: CustomPaint(painter: ArrowShape(arrowColor: Colors.white, arrowSize: 10.0)),
                            )
                          ),
                        ],
                      ),
                      const SizedBox(height: 50.0,),
                      // 操作項(xiàng)
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          // 取消發(fā)送
                          Container(
                            height: 60.0,
                            width: 60.0,
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(50.0),
                              color: voiceType == 2 ? Colors.red : Colors.black38,
                            ),
                            child: const Icon(Icons.close, color: Colors.white54,),
                          ),
                          // 語音轉(zhuǎn)文字
                          Container(
                            height: 60.0,
                            width: 60.0,
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(50.0),
                              color: voiceType == 3 ? Colors.green : Colors.black38,
                            ),
                            child: const Icon(Icons.translate, color: Colors.white54,),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              // 語音轉(zhuǎn)文字(識(shí)別結(jié)果狀態(tài))
              Positioned(
                bottom: 120,
                left: 30,
                right: 30,
                child: Visibility(
                  visible: voiceToTransfer,
                  child: Column(
                    children: [
                      // 提示結(jié)果
                      Stack(
                        children: [
                          Container(
                            height: 100.0,
                            decoration: BoxDecoration(
                              color: Colors.red,
                              borderRadius: BorderRadius.circular(15.0),
                            ),
                            child: const Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Icon(Icons.info, color: Colors.white,),
                                Text('未識(shí)別到文字。', style: TextStyle(color: Colors.white),),
                              ],
                            ),
                          ),
                          Positioned(
                            right: 35.0,
                            bottom: 1,
                            child: RotatedBox(
                              quarterTurns: 0,
                              child: CustomPaint(painter: ArrowShape(arrowColor: Colors.red, arrowSize: 10.0)),
                            )
                          ),
                        ],
                      ),
                      const SizedBox(height: 50.0,),
                      // 操作項(xiàng)
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          GestureDetector(
                            child: Container(
                              height: 60.0,
                              width: 60.0,
                              decoration: const BoxDecoration(
                                color: Colors.transparent,
                              ),
                              child: const Column(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Icon(Icons.undo, color: Colors.white54,),
                                  Text('取消', style: TextStyle(color: Colors.white70),)
                                ],
                              ),
                            ),
                            onTap: () {
                              setState(() {
                                voicePanelEnable = false;
                                voiceToTransfer = false;
                              });
                            },
                          ),
                          GestureDetector(
                            child: Container(
                              height: 60.0,
                              width: 100.0,
                              decoration: const BoxDecoration(
                                color: Colors.transparent,
                              ),
                              child: const Column(
                                mainAxisAlignment: MainAxisAlignment.center,
                                children: [
                                  Icon(Icons.graphic_eq_rounded, color: Colors.white54,),
                                  Text('發(fā)送原語音', style: TextStyle(color: Colors.white70),)
                                ],
                              ),
                            ),
                            onTap: () {},
                          ),
                          GestureDetector(
                            child: Container(
                              height: 60.0,
                              width: 60.0,
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(50.0),
                                color: Colors.white12,
                              ),
                              child: const Icon(Icons.check, color: Colors.white12,),
                            ),
                            onTap: () {},
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              // 提示文字(操作狀態(tài))
              Positioned(
                bottom: 120,
                left: 0,
                width: MediaQuery.of(context).size.width,
                child: Visibility(
                  visible: !voiceToTransfer,
                  child: Align(
                    child: Text(voiceTypeMap[voiceType], style: const TextStyle(color: Colors.white70),),
                  ),
                ),
              ),
              // 背景
              Align(
                alignment: Alignment.bottomCenter,
                child: Visibility(
                  visible: !voiceToTransfer,
                  child: Image.asset('assets/images/voice_record_bg.webp', width: double.infinity, height: 100.0, fit: BoxFit.fill),
                ),
              ),
              // 背景圖標(biāo)
              Positioned(
                bottom: 25,
                left: 0,
                width: MediaQuery.of(context).size.width,
                child: Visibility(
                  visible: !voiceToTransfer,
                  child: const Align(
                    child: Icon(Icons.graphic_eq_rounded, color: Colors.black54,),
                  ),
                ),
              ),
            ],
          ),
        ),
      )

      flutter3繪制箭頭

      聊天模塊消息及各種箭頭展示,通過flutter提供的畫板功能繪制箭頭。

      // 繪制氣泡箭頭
      class ArrowShape extends CustomPainter {
        ArrowShape({
          required this.arrowColor,
          this.arrowSize = 7,
        });
      
        final Color arrowColor; // 箭頭顏色
        final double arrowSize; // 箭頭大小
      
        @override
        void paint(Canvas canvas, Size size) {
          var paint = Paint()..color = arrowColor;
      
          var path = Path();
          path.lineTo(-arrowSize, 0);
          path.lineTo(0, arrowSize);
          path.lineTo(arrowSize, 0);
          canvas.drawPath(path, paint);
        }
      
        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
          return false;
        }
      }

      Okay,以上就是Flutter3+Dart3開發(fā)全平臺(tái)聊天App實(shí)例的一些知識(shí)分享,希望對(duì)大家有所幫助哈~~??

      Uniapp-DeepSeek跨三端AI助手|uniapp+vue3+deepseek-v3流式ai聊天模板

      Electron35-DeepSeek桌面端AI系統(tǒng)|vue3.5+electron+arco客戶端ai模板

      uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微信app聊天語音/朋友圈

      flutter3-dymall仿抖音直播商城|Flutter3.27短視頻+直播+聊天App實(shí)例

      flutter3-trip仿攜程酒店預(yù)訂|Flutter3.27+Getx預(yù)約旅游酒店App程序

      Tauri2.0-Vue3OS桌面端os平臺(tái)|tauri2+vite6+arco電腦版OS管理系統(tǒng)

      Electron31-Vue3Admin管理系統(tǒng)|vite5+electron+pinia桌面端后臺(tái)Exe

      Vite5+Electron聊天室|electron31跨平臺(tái)仿微信EXE客戶端|vue3聊天程序

      最后附上兩個(gè)最新實(shí)戰(zhàn)項(xiàng)目

      uni-app+vue3+pinia2仿抖音直播商城:http://www.rzrgm.cn/xiaoyan2017/p/17938517

      flutter3+dart3桌面端仿微信Exe聊天:http://www.rzrgm.cn/xiaoyan2017/p/18048244

       

      posted @ 2024-02-05 16:24  xiaoyan2017  閱讀(2819)  評(píng)論(4)    收藏  舉報(bào)
      友情鏈接: UP主小店B站
      主站蜘蛛池模板: 99久久精品费精品国产一区二| 国产成人免费高清激情视频| 免费看成人aa片无码视频吃奶 | 爱情岛亚洲论坛成人网站| 免费av深夜在线观看| 玖玖在线精品免费视频| 久久久久综合一本久道| 国产亚洲精品VA片在线播放| 国产办公室秘书无码精品99| 精品国产大片中文字幕| 无为县| 国产色a在线观看| av大片| 2020年最新国产精品正在播放| 99久久婷婷国产综合精品青草漫画| 成 人色 网 站 欧美大片| 亚洲av永久无码精品网站| 人妻少妇88久久中文字幕| 亚洲a免费| 无码人妻出轨黑人中文字幕| 亚洲人成电影网站色mp4| 谢通门县| 啪啪av一区二区三区| 99精品国产一区二区三区不卡| 一个人看的www视频免费观看| 亚洲国产精品日韩在线| 凉山| 国内精品大秀视频日韩精品| 一本大道久久a久久综合| 欧美成人黄在线观看| 18禁无遮挡啪啪无码网站破解版| 国产av午夜精品福利| 日本久久一区二区三区高清| 国产三级精品三级在专区| 国产日韩综合av在线| 亚洲中文字幕无码一区无广告| 日韩国产中文字幕精品| 日本道之久夂综合久久爱| 丁香花成人电影| 亚洲av无码之国产精品网址蜜芽| 尤物国产精品福利在线网|