Flutter Socket实战

FlutterSocket实战,跟着走下来,保证你也会写一个简单的聊天

在这里插入图片描述
在这里插入图片描述
欢迎加入Flutter技术交流群:723609732

前言

首先上面的功能特点读者可以看到了,由于我还没有解决如何裁剪视频第一帧这个问题,所以有点小瑕疵,但是其他功能是可以的。其中包含了:

  1. 文字聊天
  2. 图片发送(查看)
  3. 选取图片
  4. 录视频
  5. 选取视频
  6. 发送语音

接下来就步入正题开始从头给读者介绍一下整个流程,由于本文章主要介绍Socket方面的功能,所以关于消息列表的代码我这里就不放出来了,如果需要的话,请给我留言,其中可能也有我写的不好的地方,请指正出来,谢谢!


文章观看提醒:

本文章由于牵扯功能和知识点太多,所以代码的意思都写在每一行了,请读者细心观看每一行的注释,由于考虑到基础的朋友,所以注释写的比较多,请见谅~,如果有什么问题,请留言并联系我!
需要用到的插件:

  • fluttertoast:轻提示
  • flutter_screenutil:屏幕适配
  • dio:http请求
  • provide:状态管理
  • shared_preferences:本地存储
  • permission_handler:权限请求
  • image_picker:选择图片
  • photo_view:查看图片
  • flutter_ijkplayer:视频播放
  • flutter_easyrefresh:下拉刷新
  • flutter_plugin_record:语音录制播放
  • connectivity:网络状态管理

一、构思最基础的聊天页面

首先我们可以简单的想象一下微信的聊天流程,通过聊天列表进入到聊天页面后,无非都是一样的,每个聊天页面的功能都是一样的,但是发送出去的,是显示的不同的数据
在这里插入图片描述
从上面我们可以了解到,我们需要一个公用的聊天页面,这个聊天页面就是用来显示每个人不同的消息列表。
首先我是想自己写聊天页面的,但是在研究下拉刷新和上拉加载的时候,发现插件flutter_easyRefresh是自带聊天页面的,于是我便将页面copy了过来,又在之前只能聊天的基础上改动了一番。

聊天页面:ChatPage.dart

首先我要先把引入的这些东西给大家说清楚,不然你们在用的时候很容易懵掉,Flutter自己的我就不说了哈。。。

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';								// 上传文件传输Form表单时需要用到
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';		// easyRefresh插件,聊天页面的基础以及下拉刷新历史记录时会用到
import 'package:flutter_spinkit/flutter_spinkit.dart';      //Loading控件
import 'package:fluttertoast/fluttertoast.dart';			// 轻提示
import 'package:project/config/api.dart';					// 这个是我自己封装的接口,关于所有的请求都要走这里
import 'package:project/plugins/Plugins.dart';				//这个是我封装的公用方法
import 'package:project/plugins/PublicStorage.dart';		//这个是我封装的公用的本地存储的方法
import 'package:project/plugins/ScreenAdapter.dart';		//这个是我封装的公用的屏幕适配方法
import 'package:provide/provide.dart';						// 状态管理
import 'package:project/provide/SocketProvide.dart';		// socket的状态管理
import 'weChatRecoding.dart';								// 录音控件
import 'package:flutter_plugin_record/flutter_plugin_record.dart';		// 录音插件

关于上面我自己封装的方法如果各位有什么疑惑可点击下方链接看一下我以前的文章
屏幕适配以及api接口封装
本地存储封装

ChatPage.dar基础框架代码,将此代码复制下来后,应该就可以看到一个简单的聊天界面了,但是只能发送文字,并不能发送别的,
而且也只是一个没有socket的聊天,显然这并不是我们想要的,先别急,我们的界面已经出来了

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:example/generated/i18n.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

/// 聊天界面示例
class ChatPage extends StatefulWidget {
  @override
  ChatPageState createState() {
    return ChatPageState();
  }
}

class ChatPageState extends State<ChatPage> {
  // 信息列表
  List<MessageEntity> _msgList;

  // 输入框
  TextEditingController _textEditingController;
  // 滚动控制器
  ScrollController _scrollController;

  @override
  void initState() {
    super.initState();
    _msgList = [
      MessageEntity(true, "It's good!"),
      MessageEntity(false, 'EasyRefresh'),
    ];
    _textEditingController = TextEditingController();
    _textEditingController.addListener(() {
      setState(() {});
    });
    _scrollController = ScrollController();
  }

  @override
  void dispose() {
    super.dispose();
    _textEditingController.dispose();
    _scrollController.dispose();
  }

  // 发送消息
  void _sendMsg(String msg) {
    setState(() {
      _msgList.insert(0, MessageEntity(true, msg));
    });
    _scrollController.animateTo(0.0,
        duration: Duration(milliseconds: 300), curve: Curves.linear);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('KnoYo'),
        centerTitle: false,
        backgroundColor: Colors.grey[200],
        elevation: 0.0,
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.more_horiz),
            onPressed: () {
              
            },
          ),
        ],
      ),
      backgroundColor: Colors.grey[200],
      body: Column(
        children: <Widget>[
          Divider(
            height: 0.5,
          ),
          Expanded(
            flex: 1,
            child: EasyRefresh.custom(
              scrollController: _scrollController,
              reverse: true,
              footer: CustomFooter(
                  enableInfiniteLoad: true,
                  extent: 40.0,
                  triggerDistance: 50.0,
                  footerBuilder: (context,
                      loadState,
                      pulledExtent,
                      loadTriggerPullDistance,
                      loadIndicatorExtent,
                      axisDirection,
                      float,
                      completeDuration,
                      enableInfiniteLoad,
                      success,
                      noMore) {
                    return Stack(
                      children: <Widget>[
                        Positioned(
                          bottom: 0.0,
                          left: 0.0,
                          right: 0.0,
                          child: Container(
                            width: 30.0,
                            height: 30.0,
                            child: SpinKitCircle(
                              color: Colors.green,
                              size: 30.0,
                            ),
                          ),
                        ),
                      ],
                    );
                  }),
              slivers: <Widget>[
                SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (context, index) {
                      return _buildMsg(_msgList[index]);
                    },
                    childCount: _msgList.length,
                  ),
                ),
              ],
              onLoad: () async {
                await Future.delayed(Duration(seconds: 2), () {
                  if (mounted) {
                    setState(() {
                      _msgList.addAll([
                        MessageEntity(true, "It's good!"),
                        MessageEntity(false, 'EasyRefresh'),
                      ]);
                    });
                  }
                });
              },
            ),
          ),
          SafeArea(
            child: Container(
              color: Colors.grey[100],
              padding: EdgeInsets.only(
                left: 15.0,
                right: 15.0,
                top: 8.0,
                bottom: 8.0,
              ),
              child: Row(
                children: <Widget>[
                  Expanded(
                    flex: 1,
                    child: Container(
                      padding: EdgeInsets.only(
                        left: 5.0,
                        right: 5.0,
                        top: 5.0,
                        bottom: 5.0,
                      ),
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.all(Radius.circular(
                          4.0,
                        )),
                      ),
                      child: TextField(
                        controller: _textEditingController,
                        decoration: InputDecoration(
                          contentPadding: EdgeInsets.only(
                            top: 2.0,
                            bottom: 2.0,
                          ),
                          border: InputBorder.none,
                        ),
                        onSubmitted: (value) {
                          if (_textEditingController.text.isNotEmpty) {
                            _sendMsg(_textEditingController.text);
                            _textEditingController.text = '';
                          }
                        },
                      ),
                    ),
                  ),
                  InkWell(
                    onTap: () {
                      if (_textEditingController.text.isNotEmpty) {
                        _sendMsg(_textEditingController.text);
                        _textEditingController.text = '';
                      }
                    },
                    child: Container(
                      height: 30.0,
                      width: 60.0,
                      alignment: Alignment.center,
                      margin: EdgeInsets.only(
                        left: 15.0,
                      ),
                      decoration: BoxDecoration(
                        color: _textEditingController.text.isEmpty
                            ? Colors.grey
                            : Colors.green,
                        borderRadius: BorderRadius.all(Radius.circular(
                          4.0,
                        )),
                      ),
                      child: Text(
                        S.of(context).send,
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 16.0,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  // 构建消息视图
  Widget _buildMsg(MessageEntity entity) {
    if (entity == null || entity.own == null) {
      return Container();
    }
    if (entity.own) {
      return Container(
        margin: EdgeInsets.all(
          10.0,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: <Widget>[
                Text(
                  S.of(context).me,
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 13.0,
                  ),
                ),
                Container(
                  margin: EdgeInsets.only(
                    top: 5.0,
                  ),
                  padding: EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    color: Colors.lightGreen,
                    borderRadius: BorderRadius.all(Radius.circular(
                      4.0,
                    )),
                  ),
                  constraints: BoxConstraints(
                    maxWidth: 200.0,
                  ),
                  child: Text(
                    entity.msg ?? '',
                    overflow: TextOverflow.clip,
                    style: TextStyle(
                      fontSize: 16.0,
                    ),
                  ),
                )
              ],
            ),
            Card(
              margin: EdgeInsets.only(
                left: 10.0,
              ),
              clipBehavior: Clip.hardEdge,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(20.0)),
              ),
              elevation: 0.0,
              child: Container(
                height: 40.0,
                width: 40.0,
                child: Image.asset('assets/image/head.jpg'),
              ),
            ),
          ],
        ),
      );
    } else {
      return Container(
        margin: EdgeInsets.all(
          10.0,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Card(
              margin: EdgeInsets.only(
                right: 10.0,
              ),
              clipBehavior: Clip.hardEdge,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(20.0)),
              ),
              elevation: 0.0,
              child: Container(
                height: 40.0,
                width: 40.0,
                child: Image.asset('assets/image/head_knoyo.jpg'),
              ),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  'KnoYo',
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 13.0,
                  ),
                ),
                Container(
                  margin: EdgeInsets.only(
                    top: 5.0,
                  ),
                  padding: EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.all(Radius.circular(
                      4.0,
                    )),
                  ),
                  constraints: BoxConstraints(
                    maxWidth: 200.0,
                  ),
                  child: Text(
                    entity.msg ?? '',
                    overflow: TextOverflow.clip,
                    style: TextStyle(
                      fontSize: 16.0,
                    ),
                  ),
                )
              ],
            ),
          ],
        ),
      );
    }
  }
}

/// 信息实体
class MessageEntity {
  bool own;
  String msg;

  MessageEntity(this.own, this.msg);
}

二、创建provide消息实体

关于provide方面的知识我就多介绍了,不清楚的读者请查看我的这篇文章:Ftter状态持久化以及状态管理

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:project/config/socket.dart';
import 'package:project/plugins/PublicStorage.dart';

class SocketProvider with ChangeNotifier{

    int a = 1;
    Socket socket;                              // 存储socket实例
    String netWorkstate = 'none';              // 网络状态
    List<ChatRecord> records = List<ChatRecord>();

    // 存储socket实例
    setSocket(val){
      this.socket = val;
      notifyListeners();
    }

    // 存储发送来的消息
    // 私聊消息,消息类型, 是不是我发送的,语音时间长度, 是否需要显示成网络信息, 是不是历史记录存储
    setRecords(message,String type, bool newIsMe,{String time_length, history:false}){
      records..insert(0, ChatRecord(message: message,type:type, newIsMe: newIsMe, time_length:time_length));
      notifyListeners();
    }

    // 清空消息页面
    clearRecords(){
      records = [];
    }

    // 设置当前网络状态
    setnetWorkState(val, context) async {

      var socket = new ClientSocket();            // 在这里实例化socket,这里是整个APP的socket调用最开始的地方

      var token = await PublicStorage.getHistoryList('token');    // 获取本地token

      // 只有当之前没有网络的时候,从新有了网络后并且token存在的情况下才会从新触发socket通信
      // 默认的时候是none 所以初始化第一次打开的时候是可以触发的
      if(token.isNotEmpty && netWorkstate == 'none' && (val == '4G' || val == 'Wifi')){
        print('-----------------------------链接socket-------------------------');
        
        socket.Connect(context);
      }
      // 设置网络状态
      netWorkstate = val;
      notifyListeners();
    }

    // 获取当前网络状态
    getnetWorkState(){
      return netWorkstate;
    }
}

// 私聊发送消息内容数据
class ChatRecord{
  var message;                                  // 消息内容
  String time_length;                           // 语音时长
  String type;                                  // 消息类型
  bool newIsMe;                                 // 是不是我发送的
  ChatRecord({this.message, this.type, this.newIsMe, this.time_length});
}

设置网络状态页面:Tab.dart

由于该页面涉及太多代码,我只粘贴设置网络的那段,也就是调用setnetWorkState的那一段,直接在initState中调用即可,_state变量可以写一下,用来记录网络的,虽然用不到。。。

_listenNetWork(){
    _subscription = Connectivity()
        .onConnectivityChanged
        .listen((ConnectivityResult result) {
      if (result == ConnectivityResult.mobile) {
        Provide.value<SocketProvider>(context).setnetWorkState('4G', context);
        setState(() {
          _state = "手机网络";
        });
// I am connected to a mobile network.
      } else if (result == ConnectivityResult.wifi) {
        Provide.value<SocketProvider>(context).setnetWorkState('Wifi', context);
        setState(() {
          _state = "Wifi 网络";
        });
      } else {
        Fluttertoast.showToast(msg: '网络不稳定~ 请检查!');
        Provide.value<SocketProvider>(context).setnetWorkState('none', context);
        setState(() {
          _state = "没有网络";
        });
      }
    });
  }
三、实例化,公用化socket

接下来我们开始写socket的封装了,由于聊天功能是需要在一开始启动APP时,就要连接服务器,所以我们来把socket写到provide状态管理当中,这样,我们其他的页面也能通过方法来调用到socket的实例了,还有就是写socket中需要注意的几个问题,我先列出来,具体可以看代码

  • App启动时就要连接socket
  • 连接socket前需要判断本地是否有token以及网络状态(由于需要判断网络状态,所以需要安装个网络状态的插件connectivity
  • socket需要接受到服务器发来 数据信息来进行相应的处理
  • socket在出现断开的问题后的应对措施
  • socket需要向后台每隔15秒发送一次心跳,以保持socket的活跃度,发送心跳时,也要监听网络状态以及本地token

主要代码

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:connectivity/connectivity.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:project/plugins/Plugins.dart';
import 'package:provide/provide.dart';
import 'package:project/provide/SocketProvide.dart';
import 'package:project/plugins/PublicStorage.dart';
import 'package:provide/provide.dart';
import 'package:project/provide/userinformation.dart';

import 'api.dart';

class ClientSocket {
   var gl_sock;             // 拿到socket实例,存储到provide中,便于其他页面使用socket的方法

   var response;            // 接收socket返回回来的数据

   var gl_context;          // 存储传入进来的context,provide的方法在调用时需要传

   var token;               // 获取本地token

   int commandTime = 15;        // 向后台发送心跳的时间
  
   Timer timer;             

   bool netWorkStatus = true;     // 网络状态

   bool socketStatus = false;     // socket状态

  
  // 初始化socket连接
   void Connect(context) async {
    
    // 获取本地存储Token
    token = await PublicStorage.getHistoryList('token');
    
    // 判断token是否为空并且网络状态是否为没有网络:停止心跳发送
    if(token.isEmpty || !netWorkStatus){
      timer = null;
      return;
    }

    

    //创建一个Socket连接到指定地址与端口
    await Socket.connect('11.111.111.11', 9500).then((socket){ 
      print('---------连接成功------------');
      
      socketStatus = true;
      
      gl_sock = socket;
      
      gl_context = context;

      // 存储全局socket对象
      Provide.value<SocketProvider>(context).setSocket(gl_sock);
      // 全局的socket设置为在线状态,该方法由项目决定酌情添加
      // Provide.value<SocketProvider>(context).setOnlineSocket(true);


      // 向服务器发送token验证
      Map arguments = {
        "type":"verify_token",
        "token":token[0]
      };

      
      gl_sock.write(json.encode(arguments));
      
      // socket监听
      gl_sock.listen(dataHandler,
            // onError: errorHandler,
            onDone: doneHandler,
            cancelOnError: false);
      // gl_sock.close();
    }).catchError((e) {
      print("socket无法连接: $e");
    });

  }

  // 接收socket返回报文
   dataHandler(data) async {
    print('-------Socket发送来的消息-------');
    var cnData = await utf8.decode(data);       // 将信息转为正常文字
    response = json.decode(cnData.toString());

    print(response);

    // 判断返回的状态信息token验证是否成功,如果相等,变可以socket通信
    if(response['type'] == 'verify_success'){
      Fluttertoast.showToast(msg: '欢迎登录~');

      // 给后台发送心跳
      heartbeatSocket();
      return;
    }

    
      // 判断 服务器返回的接收人id和发送人id如果不是同一个的话,开始进行socket信息的存储
      // recv_id:接收人、send_id:发送人
      if(response['recv_id'] != int.parse(response['send_id'])){
		// 判断消息类型,存储到provide消息实体当中
        switch (response['content_type']) {
          case 'text':
              Provide.value<SocketProvider>(gl_context).setRecords(response['Content'], 'text',  false);
            break;
          case 'img':
              Provide.value<SocketProvider>(gl_context).setRecords(response['Content'],'img', false);
            break;
          case 'video':
              Provide.value<SocketProvider>(gl_context).setRecords(response['Content'],'video',  false);      
            break;
          case 'audio':
              Provide.value<SocketProvider>(gl_context).setRecords(response['Content'],'audio', false, time_length:response['time_length']);
            break;
          default:
        }
      }
    
  }
  

 


  // Socket出现断开的问题
  void doneHandler(){
    socketStatus = false;
    Fluttertoast.cancel();      // 清空所有弹窗
    reconnectSocket();          //调用重连socket方法
  }
  
  // 重新连接socket
  void reconnectSocket(){
    int count = 0;
    const period = const Duration(seconds: 1);
    // 定时器
    Timer.periodic(period, (timer) {
      // 每一次重连之前,都删除关掉上一个socket
      // gl_sock.close();
      gl_sock = null;
      count++;
      if(count >= 3){
        print('时间到了!!!开始从连socket');
        // 链接socket
        Connect(gl_context);      // 重连
        count = 0;                // 倒计时设置为0
        timer.cancel();           // 关闭倒计时
        timer = null;             // 清空倒计时
        Fluttertoast.cancel();    // 关闭弹框
      }
    });
  }


  // 心跳机,每15秒给后台发送一次,用来保持连接
  void heartbeatSocket(){
    
    const duration =  Duration(seconds:1);
    
    var callback = (time) async {
      // 如果socket状态是断开的,就停止定时器
      if(!socketStatus){
        time.cancel();
      }
      var _subscription = Connectivity()
                        .onConnectivityChanged
                        .listen((ConnectivityResult result) {
                          // print('--------------------当前网络:$result------------------------');
                      if (result != ConnectivityResult.mobile && result != ConnectivityResult.wifi) {
                        print('没有网络,停止定时器');
                        netWorkStatus = false;
                        time.cancel();
                      }else{
                        netWorkStatus = true;
                      }
                    });
    
      token = await PublicStorage.getHistoryList('token');

      // token为空,关闭定时器
      if(token.isEmpty){
        time.cancel();
        return;
      }

      if(commandTime < 1){
        print('-----------------发送心跳------------------');
        Map arguments = {
          "type":"heartbeat",
        };
        gl_sock.write(json.encode(arguments));

        commandTime = 15;
      }else{
        commandTime--;
      }
    };

    timer = Timer.periodic(duration, callback);
  }


}
三、改造chatPage.dart

现在我们的socket已经写好了,消息实体Provide类我们也已经写好了,现在就是将我们消息实体Provide里面存储的数据展示到页面上了

前提声明:

  • 下面代码中出现的arguments大多数是我从上个页面传过来的参数,其中包括:用户头像,用户名,用户id
  • 代码中出现的 Plugins.的方法,都是我自己封装的方法,在后面会贴给大家
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:project/config/api.dart';
import 'package:project/plugins/Plugins.dart';
import 'package:project/plugins/PublicStorage.dart';
import 'package:project/plugins/ScreenAdapter.dart';
import 'package:provide/provide.dart';
import 'package:project/provide/SocketProvide.dart';
import 'weChatRecoding.dart';
import 'package:flutter_plugin_record/flutter_plugin_record.dart';

/// 聊天界面
class ChatPage extends StatefulWidget {
  final arguments;
  ChatPage(this.arguments);
  @override
  ChatPageState createState() {
    return ChatPageState(this.arguments);
  }
}

class ChatPageState extends State<ChatPage> {
  // 这里注意一下这个arguments,是我们用来接收上个页面传来的用户名以及头像
  final arguments;
  ChatPageState(this.arguments);
  var base_url = 'https://flutter.ikuer.cn'; 
  var api = new Api();
  var userInfo;
  bool soundRecording = false;      // 是否显示语音按钮
  double _height = 0;               // 控制操作栏的高度
  var gl_socket;
  var localImage, localVideo;       // 本地图片和视频
  var audioPath;                    // 语音路径
  var duration = Duration(milliseconds: 200);
  

  // 输入框
  TextEditingController _textEditingController;
  // 滚动控制器
  ScrollController _scrollController;
  // 语音播放控制器
  FlutterPluginRecord recordPlugin;

  // ClientSocket socket;

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

    _textEditingController = TextEditingController();
    _scrollController = ScrollController();
    recordPlugin =  FlutterPluginRecord();
  
  }

  @override
  void dispose() {
    _textEditingController.dispose();
    _scrollController.dispose();
    recordPlugin.dispose();
    super.dispose();
  }

  // 获取用户数据(这个方法读者可以酌情处理需不需要,可以全局搜索一下userInfo这个变量的用到的地方再决定)
   _setUserData(){
    api.getData(context, 'userInfo').then((val){
      if(val == null){
        return;
      }
      var response = json.decode(val.toString());
      print('--------------获取服务器用户数据----------------');
      print(response);
      setState(() {
        userInfo = response;
      });
      PublicStorage.setHistoryList('UserInfo', response);
    }); 
  }

  // 发送消息
  void _sendMsg(String msg) {
    // 存储文字消息
    Provide.value<SocketProvider>(context).setRecords(msg,'text', true);
    _scrollController.animateTo(0.0, duration: Duration(milliseconds: 300), curve: Curves.linear);
  }

  startRecord(){
    print("111开始录制");
  }

  stopRecord(String path,double audioTimeLength ) async {
    print("结束束录制");
    print("音频文件位置"+path);
    print("音频录制时长"+audioTimeLength.toString());
    setState(() {
      this.audioPath = path;
    });
    
    api.postData(context, 'uploadFile', formData: await FormData1(path)).then((data){
      var audiourl = json.decode(data.toString())['file_path'];
      Map contentArguments = {
        'type':'private_chat',
        'content_type':'audio',
        'Content':audiourl,
        'time_length':audioTimeLength.toString(),
        'recv_id':arguments['recv_id']
      };
      print(contentArguments);
      // socket发送消息
      gl_socket.write(json.encode(contentArguments));
      // 存储语音文件
      Provide.value<SocketProvider>(context).setRecords(path,'audio', true, time_length:audioTimeLength.toString());

    });
  
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(icon: Icon(IconData(0xe622, fontFamily: 'myIcon'), size: ScreenAdapter.size(40),), onPressed: ()=>Navigator.pop(context),),
          title: Container(
            child:  Text('${arguments['nickname']}', style: TextStyle(fontSize: ScreenAdapter.size(35)),),
          ),
        centerTitle: true,
        // backgroundColor: Colors.grey[200],
        elevation: 0.0,
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.people),
            onPressed: (){
              print('更多');
            },
          )
        ],
      ),
      backgroundColor: Colors.grey[200],
      body: Provide<SocketProvider>(
        builder: (context, child, val){
          return Container(
            color: Colors.white,
            child:  Column(
                  children: <Widget>[
                    Divider(
                      height: 0.5,
                    ),
                    Expanded(
                      flex: 1,
                      child: InkWell(
                        child: EasyRefresh.custom(
                        scrollController: _scrollController,
                        reverse: true,
                        footer: CustomFooter(
                            enableInfiniteLoad: false,
                            extent: 40.0,
                            triggerDistance: 50.0,
                            footerBuilder: (context,
                                loadState,
                                pulledExtent,
                                loadTriggerPullDistance,
                                loadIndicatorExtent,
                                axisDirection,
                                float,
                                completeDuration,
                                enableInfiniteLoad,
                                success,
                                noMore) {
                              return Stack(
                                children: <Widget>[
                                  Positioned(
                                    bottom: 0.0,
                                    left: 0.0,
                                    right: 0.0,
                                    child: Container(
                                      width: 30.0,
                                      height: 30.0,
                                      child: SpinKitCircle(
                                        color: Colors.green,
                                        size: 30.0,
                                      ),
                                    ),
                                  ),
                                ],
                              );
                            }),
                        slivers: <Widget>[
                          SliverList(
                            delegate: SliverChildBuilderDelegate(
                              (context, index) {
                                return _buildMsg(val.records[index]);
                              },
                              childCount: val.records.length,
                            ),
                          ),
                        ],
                        onLoad: () async {
                          

                        },
                      ),
                      onTap: (){
                        setState(() {
                          _height = 0;
                        });
                      },
                      )
                    ),
                    
                    SafeArea(
                      child: Container(
                        color: Colors.grey[100],
                        padding: EdgeInsets.only(
                          left: 15.0,
                          right: 15.0,
                          top: 8.0,
                          bottom: 8.0,
                        ),
                        child: Row(
                          children: <Widget>[
                            InkWell(
                              child: Container(
                                margin: EdgeInsets.only(right: ScreenAdapter.setWidth(10)),
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(50),
                                  ),
                                  child: Icon(Icons.keyboard_voice, color: Colors.grey,),
                                ),
                              onTap: (){
                                setState(() {
                                  this._height = 0;
                                  this.gl_socket = val.socket;
                                  this.soundRecording = !this.soundRecording;
                                });
                              },
                            ),
                            soundRecording ?
                            Expanded(
                              flex: 1,
                              child: VoiceWidget(startRecord: startRecord,stopRecord: stopRecord),
                            )
                            :
                            Expanded(
                              flex: 1,
                              child: Container(
                                padding: EdgeInsets.only(
                                  left: 5.0,
                                  right: 5.0,
                                  top: 5.0,
                                  bottom: 5.0,
                                ),
                                decoration: BoxDecoration(
                                  color: Colors.white,
                                  borderRadius: BorderRadius.all(Radius.circular(
                                  20,
                                  )),
                                ),
                                child: TextField(
                                  controller: _textEditingController,
                                  decoration: InputDecoration(
                                    contentPadding: EdgeInsets.only(
                                      top: 2.0,
                                      bottom: 2.0,
                                    ),
                                    border: InputBorder.none,
                                  ),
                                  onChanged: (val){
                                    
                                  },
                                  onSubmitted: (value) {
                                    if (_textEditingController.text.isNotEmpty) {
                                      _sendMsg(_textEditingController.text);
                                      _textEditingController.text = '';
                                    }
                                  },
                                  onTap: (){
                                    setState(() {
                                      _height = 0;
                                    });
                                  },
                                ),
                              ),
                            ),
                            InkWell(
                              child: Container(
                                margin: EdgeInsets.symmetric(horizontal: ScreenAdapter.setWidth(15)),
                                decoration: BoxDecoration(
                                  // color: Colors.grey,
                                  borderRadius: BorderRadius.circular(50),
                                  border: Border.all(width: ScreenAdapter.setWidth(0.5), color: Colors.grey)
                                ),
                                child: Icon(Icons.add, color: Colors.grey,),
                              ),
                              onTap: (){
                                // 收起键盘
                                FocusScope.of(context).requestFocus(FocusNode());
                                setState(() {
                                  this.soundRecording = false;
                                  _height = 100;
                                });
                              },
                            ),
                            // :
                            InkWell(
                              onTap: () {
                                if(_textEditingController.text.isEmpty){
                                  return;
                                }
                                Map contentArguments = {
                                  'type':'private_chat',
                                  'content_type':'text',
                                  'Content':_textEditingController.text,
                                  'recv_id':arguments['recv_id']
                                };
                                print(contentArguments);
                                if (_textEditingController.text.isNotEmpty) {
                                  _sendMsg(_textEditingController.text);
                                  _textEditingController.text = '';
                                }
                                // socket发送消息
                                val.socket.write(json.encode(contentArguments));
                              },
                              child: Container(
                                height: 30.0,
                                width: 60.0,
                                alignment: Alignment.center,
                                // margin: EdgeInsets.only(
                                //   left: ScreenAdapter.setWidth(10),
                                // ),
                                decoration: BoxDecoration(
                                  // color: _textEditingController.text.isEmpty
                                  //     ? Colors.grey
                                  //     : Colors.green,
                                  color: Color(0xff4ADDFE),
                                  borderRadius: BorderRadius.all(Radius.circular(
                                   20,
                                  )),
                                ),
                                child: Text(
                                  '发送',
                                  // S.of(context).send,
                                  style: TextStyle(
                                    color: Colors.white,
                                    fontSize: ScreenAdapter.size(30)
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                    AnimatedContainer(
                      duration: duration,
                      height: _height,
                      width: MediaQuery.of(context).size.width,
                      color: Colors.white,
                      child: Container(
                        child: GridView(
                          padding: EdgeInsets.zero,
                          gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                              maxCrossAxisExtent: 120.0,
                              childAspectRatio: 1.0 //宽高比为2
                          ),
                          children: <Widget>[
                           IconButton(
                             icon: Icon(Icons.camera_alt),
                             onPressed: () async {
                               var img_url = await Plugins.takePhoto();
                               if(img_url!=null){
                                setState(() {
                                  localImage = img_url;
                                });
                                api.postData(context, 'uploadFile', formData: await FormData1(img_url.path)).then((data){
                                  var imgurl = json.decode(data.toString())['file_path'];
                                  Map contentArguments = {
                                    'type':'private_chat',
                                    'content_type':'img',
                                    'Content':imgurl,
                                    'recv_id':arguments['recv_id']
                                  };
                                  print(contentArguments);
                                  Provide.value<SocketProvider>(context).setRecords(localImage,'img', true);
                                  // socket发送消息
                                  val.socket.write(json.encode(contentArguments));
                                });
                              }
                             },
                           ),
                           IconButton(
                             icon: Icon(Icons.photo),
                             onPressed: () async {
                               print('---------------------选择图片---------------------');
                               var img_url = await Plugins.openGallery();
                               if(img_url!=null){
                                setState(() {
                                  localImage = img_url;
                                });
                                print(localImage);
                                api.postData(context, 'uploadFile', formData: await FormData1(img_url.path)).then((data){
                                  var imgurl = json.decode(data.toString())['file_path'];
                                  print('路径');
                                  print(imgurl);
                                  Map contentArguments = {
                                    'type':'private_chat',
                                    'content_type':'img',
                                    'Content':imgurl,
                                    'recv_id':arguments['recv_id']
                                  };
                                  print(json.encode(contentArguments));
                                  Provide.value<SocketProvider>(context).setRecords(localImage,'img', true);
                                  // // socket发送消息
                                  val.socket.write(json.encode(contentArguments));
                                });
                              }
                             },
                           ),
                           IconButton(
                             icon: Icon(Icons.videocam),
                             onPressed: () async {
                               print('录像');
                               var video_url = await Plugins.takeVideo();
                               if(video_url!=null){
                                setState(() {
                                  localVideo = video_url;
                                });
                                api.postData(context, 'uploadFile', formData: await FormData1(video_url.path)).then((data){
                                  var videourl = json.decode(data.toString())['file_path'];
                                  Map contentArguments = {
                                    'type':'private_chat',
                                    'content_type':'video',
                                    'Content':videourl,
                                    'recv_id':arguments['recv_id']
                                  };
                                  print(contentArguments);
                                  Provide.value<SocketProvider>(context).setRecords(localVideo.path,'video', true);
                                  // socket发送消息
                                  val.socket.write(json.encode(contentArguments));
                                });
                              }
                             },
                           ),
                           IconButton(
                             icon: Icon(Icons.movie),
                             onPressed: () async {
                               print('发送视频');
                               var video_url = await Plugins.getVideo();
                              //  print(video_url);
                               if(video_url!=null){
                                setState(() {
                                  localVideo = video_url;
                                });
                                
                                api.postData(context, 'uploadFile', formData: await FormData1(video_url.path)).then((data){
                                  var videourl = json.decode(data.toString())['file_path'];
                                  Map contentArguments = {
                                    'type':'private_chat',
                                    'content_type':'video',
                                    'Content':videourl,
                                    'recv_id':arguments['recv_id']
                                  };
                                  print(contentArguments);
                                  Provide.value<SocketProvider>(context).setRecords(localVideo.path,'video', true);
                                  // socket发送消息
                                  val.socket.write(json.encode(contentArguments));
                                });
                              }
                             },
                           ),
                          ],
                        )
                      )
                    ),
                  ],
                )
       ,
          );
         
        },
      )
    );
  }

  // 构建消息视图
  Widget _buildMsg(ChatRecord entity) {
    if (entity == null || entity.newIsMe == null || userInfo == null) {
      return Container();
    }
    if (entity.newIsMe) {
      return Container(
        margin: EdgeInsets.all(
          10.0,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: <Widget>[
                Container(
                  margin: EdgeInsets.only(
                    top: 5.0,
                  ),
                  padding: EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    color: entity.type == 'text' || entity.type == 'audio' ? Color(0xff4ADDFE) : null,
                    borderRadius: BorderRadius.all(Radius.circular(
                      4.0,
                    )),
                  ),
                  constraints: BoxConstraints(
                    maxWidth: 300.0,
                    maxHeight: 300.0
                  ),
                  child: MessageWidget(entity)
                )
              ],
            ),
            Card(
              margin: EdgeInsets.only(
                left: 10.0,
              ),
              clipBehavior: Clip.hardEdge,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(20.0)),
              ),
              elevation: 0.0,
              child: Container(
                height: 40.0,
                width: 40.0,
                child: Image.network('$base_url${userInfo['head_pic']}', fit: BoxFit.cover,),
              ),
            ),
          ],
        ),
      );
    } else {
      return Container(
        margin: EdgeInsets.all(
          10.0,
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Card(
              margin: EdgeInsets.only(
                right: 10.0,
              ),
              clipBehavior: Clip.hardEdge,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.all(Radius.circular(20.0)),
              ),
              elevation: 0.0,
              child: Container(
                height: 40.0,
                width: 40.0,
                child: Image.network('$base_url${arguments['head_pic']}', fit: BoxFit.cover,),
              ),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                // Text(
                //   '${arguments['nickname']}',
                //   style: TextStyle(
                //     color: Colors.grey,
                //     fontSize: 13.0,
                //   ),
                // ),
                Container(
                  margin: EdgeInsets.only(
                    top: 5.0,
                  ),
                  padding: EdgeInsets.all(10.0),
                  decoration: BoxDecoration(
                    color: entity.type == 'text' || entity.type == 'audio' ? Colors.black12 : null,
                    borderRadius: BorderRadius.all(Radius.circular(
                      4.0,
                    )),
                  ),
                  constraints: BoxConstraints(
                    maxWidth: 300.0,
                  ),
                  child: MessageWidget(entity)
                )
              ],
            ),
          ],
        ),
      );
    }
  }

  // 判断显示什么类型的消息
  Widget MessageWidget(entity){
    switch (entity.type) {
      case 'text':
        return Text(
                entity.message ?? '',
                overflow: TextOverflow.clip,
                style: TextStyle(
                  color: entity.newIsMe ? Colors.white : Colors.black,
                  fontSize: 16.0,
                ),
              );
        break;
      case 'img':
        return entity.newIsMe  ? 
          InkWell(
            child: Image.file(entity.message),
            onTap: (){
              print('点了图片');
              Map arguments = {
                'imageProvider':FileImage(entity.message),
                'heroTag':'simple'
              };
              Navigator.pushNamed(context, '/image', arguments: arguments);
            },
          )
         : 
         InkWell(
           child: Image.network('$base_url${entity.message}'),
           onTap: (){
             Map arguments = {
                'imageProvider':NetworkImage('$base_url${entity.message}'),
                'heroTag':'simple'
              };
              Navigator.pushNamed(context, '/image', arguments: arguments);
           },
         );
        break;
      case 'video':
        return  InkWell(
          child: Container(
            width: ScreenAdapter.setWidth(300),
            height: ScreenAdapter.setHeight(150),
            decoration: BoxDecoration(
              color: Colors.grey,
              borderRadius: BorderRadius.circular(10)
            ),
            alignment: Alignment.center,
            child: Icon(Icons.play_arrow, color: Colors.white,),
          ),
          onTap: (){
            Map arguments = {
              'isMe':entity.newIsMe,
              'message':entity.message
            };
            
            
            Navigator.pushNamed(context, '/video', arguments: arguments);
          },
        );
        break;
      case 'audio':
        return Container(
            width: ScreenAdapter.setWidth(160),
            child: entity.newIsMe ?
            InkWell(
              onTap: (){
                
                playByPath(entity.message);
              },
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text('${entity.time_length}'),
                  Container(
                    margin: EdgeInsets.only(left: ScreenAdapter.setWidth(10)),
                    width: ScreenAdapter.setWidth(50),
                    height: ScreenAdapter.setHeight(30),
                    // color: Colors.red,
                    child: Image.asset('assets/images/recording_right.png', fit: BoxFit.cover,),
                  )
                ],
              ),
            )
            :
             InkWell(
               onTap: (){
                  // socket发送来的语音
                  playByPath('$base_url${entity.message}');
               },
               child: Row(
                  children: <Widget>[
                    Text('${entity.time_length}'),
                    Container(
                      margin: EdgeInsets.only(left: ScreenAdapter.setWidth(10)),
                      width: ScreenAdapter.setWidth(50),
                      height: ScreenAdapter.setHeight(30),
                      // color: Colors.red,
                      child: Image.asset('assets/images/recording_left.png', fit: BoxFit.cover,),
                    )
                  ],
                ),
             )
            
          );
        break;
      default:
    }
  }


    ///播放指定路径录音文件
  void playByPath(String path) {
    recordPlugin.playByPath(path);
  }

   // dio上传文件FormData格式
  Future<FormData> FormData1(fileUrl) async {
    return FormData.fromMap({
      "file": await MultipartFile.fromFile(fileUrl)
    });
  }
}

weChatRecoding.dart:语音发送悬浮窗
import 'package:flutter/material.dart';
import 'package:flutter_plugin_record/flutter_plugin_record.dart';
import 'package:project/plugins/ScreenAdapter.dart';

typedef startRecord = Future Function();
typedef stopRecord = Future Function();

class VoiceWidget extends StatefulWidget {
  final Function startRecord;
  final Function stopRecord;

  /// startRecord 开始录制回调  stopRecord回调
  const VoiceWidget({Key key, this.startRecord, this.stopRecord})
      : super(key: key);

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

class _VoiceWidgetState extends State<VoiceWidget> {
  double starty = 0.0;
  double offset = 0.0;
  bool isUp = false;
  String textShow = "按住说话";
  String toastShow = "手指上滑,取消发送";
  String voiceIco = "images/voice_volume_1.png";

  ///默认隐藏状态
  bool voiceState = true;
  OverlayEntry overlayEntry;
  FlutterPluginRecord recordPlugin;

  @override
  void initState() {
    super.initState();
    recordPlugin = new FlutterPluginRecord();

    _init();

    ///初始化方法的监听
    recordPlugin.responseFromInit.listen((data) {
      if (data) {
        print("初始化成功");
      } else {
        print("初始化失败");
      }
    });

    /// 开始录制或结束录制的监听
    recordPlugin.response.listen((data) {
      if (data.msg == "onStop") {
        ///结束录制时会返回录制文件的地址方便上传服务器
        print("onStop  " + data.path);
        widget.stopRecord(data.path, data.audioTimeLength);
      } else if (data.msg == "onStart") {
        print("onStart --");
        widget.startRecord();
      }
    });

    ///录制过程监听录制的声音的大小 方便做语音动画显示图片的样式
    recordPlugin.responseFromAmplitude.listen((data) {
      var voiceData = double.parse(data.msg);
      setState(() {
        if (voiceData > 0 && voiceData < 0.1) {
          voiceIco = "images/voice_volume_2.png";
        } else if (voiceData > 0.2 && voiceData < 0.3) {
          voiceIco = "images/voice_volume_3.png";
        } else if (voiceData > 0.3 && voiceData < 0.4) {
          voiceIco = "images/voice_volume_4.png";
        } else if (voiceData > 0.4 && voiceData < 0.5) {
          voiceIco = "images/voice_volume_5.png";
        } else if (voiceData > 0.5 && voiceData < 0.6) {
          voiceIco = "images/voice_volume_6.png";
        } else if (voiceData > 0.6 && voiceData < 0.7) {
          voiceIco = "images/voice_volume_7.png";
        } else if (voiceData > 0.7 && voiceData < 1) {
          voiceIco = "images/voice_volume_7.png";
        } else {
          voiceIco = "images/voice_volume_1.png";
        }
        if (overlayEntry != null) {
          overlayEntry.markNeedsBuild();
        }
      });

      print("振幅大小   " + voiceData.toString() + "  " + voiceIco);
    });
  }

  ///显示录音悬浮布局
  buildOverLayView(BuildContext context) {
    if (overlayEntry == null) {
      overlayEntry = new OverlayEntry(builder: (content) {
        return Positioned(
          top: MediaQuery.of(context).size.height * 0.5 - 80,
          left: MediaQuery.of(context).size.width * 0.5 - 80,
          child: Material(
            type: MaterialType.transparency,
            child: Center(
              child: Opacity(
                opacity: 0.8,
                child: Container(
                  width: 160,
                  height: 160,
                  decoration: BoxDecoration(
                    color: Color(0xff77797A),
                    borderRadius: BorderRadius.all(Radius.circular(20.0)),
                  ),
                  child: Column(
                    children: <Widget>[
                      Container(
                        margin: EdgeInsets.only(top: 10),
                        child: new Image.asset(
                          voiceIco,
                          width: 100,
                          height: 100,
                          package: 'flutter_plugin_record',
                        ),
                      ),
                      Container(
//                      padding: EdgeInsets.only(right: 20, left: 20, top: 0),
                        child: Text(
                          toastShow,
                          style: TextStyle(
                            fontStyle: FontStyle.normal,
                            color: Colors.white,
                            fontSize: 14,
                          ),
                        ),
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
        );
      });
      Overlay.of(context).insert(overlayEntry);
    }
  }

  showVoiceView() {
    setState(() {
      textShow = "松开结束";
      voiceState = false;
    });
    buildOverLayView(context);
    start();
  }

  hideVoiceView() {
    setState(() {
      textShow = "按住说话";
      voiceState = true;
    });

    stop();
    if (overlayEntry != null) {
      overlayEntry.remove();
      overlayEntry = null;
    }

    if (isUp) {
      print("取消发送");
    } else {
      print("进行发送");
    }
  }

  moveVoiceView() {
    // print(offset - start);
    setState(() {
      isUp = starty - offset > 100 ? true : false;
      if (isUp) {
        textShow = "松开手指,取消发送";
        toastShow = textShow;
      } else {
        textShow = "松开结束";
        toastShow = "手指上滑,取消发送";
      }
    });
  }

  ///初始化语音录制的方法
  void _init() async {
    recordPlugin.init();
  }

  ///开始语音录制的方法
  void start() async {
    recordPlugin.start();
  }

  ///停止语音录制的方法
  void stop() {
    recordPlugin.stop();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: GestureDetector(
        onVerticalDragStart: (details) {
          starty = details.globalPosition.dy;
          showVoiceView();
        },
        onVerticalDragEnd: (details) {
          hideVoiceView();
        },
        onVerticalDragUpdate: (details) {
          offset = details.globalPosition.dy;
          moveVoiceView();
        },
        child: Container(
          height: ScreenAdapter.setHeight(50),
          decoration: BoxDecoration(
            border: Border.all(width: 0.5, color: Colors.grey)
          ),
          child: Center(
            child: Text(
              textShow,
              style: TextStyle(fontSize: ScreenAdapter.size(30)),
            ),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    if (recordPlugin != null) {
      recordPlugin.dispose();
    }
    super.dispose();
  }
}

Plugins.dart
import 'package:permission_handler/permission_handler.dart'; // 权限申请
import 'package:image_picker/image_picker.dart'; // 选择图片
class Plugins{
/*拍照*/
  static takePhoto() async {
    var image = await ImagePicker.pickImage(source: ImageSource.camera);
    // print('拍照返回:' + image.toString());
    return image;
  }

  /*相册*/
  static openGallery() async {
    var image = await ImagePicker.pickImage(source: ImageSource.gallery);
    // print('相册返回:' + image.toString());
    return image;
  }

  /*选取视频*/
  static getVideo() async {
    var video = await ImagePicker.pickVideo(source: ImageSource.gallery);
    return video;
  }

  /*拍摄视频*/
  static takeVideo() async {
    var video = await ImagePicker.pickVideo(source: ImageSource.camera);
    // print('拍摄视频:' + image.toString());
    return video;
  }
}
showImag.dart图片
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';

class PhotoViewSimpleScreen extends StatelessWidget{

    

    // const PhotoViewSimpleScreen({
    //   this.aruments;
    //     this.imageProvider,//图片
    //     this.loadingChild,//加载时的widget
    //     this.backgroundDecoration,//背景修饰
    //     this.minScale,//最大缩放倍数
    //     this.maxScale,//最小缩放倍数
    //     this.heroTag,//hero动画tagid
    // });
    Map arguments;
    PhotoViewSimpleScreen(
      this.arguments
    );
    // final ImageProvider imageProvider;
    // final Widget loadingChild;
    // final Decoration backgroundDecoration;
    // final dynamic minScale;
    // final dynamic maxScale;
    // final String heroTag;

    @override
    Widget build(BuildContext context) {
        return Scaffold(
            body: InkWell(
              onTap: (){
                Navigator.pop(context);
              },
              child: Container(
                constraints: BoxConstraints.expand(
                    height: MediaQuery.of(context).size.height,
                ),
                child: Stack(
                    children: <Widget>[
                        Positioned(
                            top: 0,
                            left: 0,
                            bottom: 0,
                            right: 0,
                            child: PhotoView(
                                imageProvider: arguments['imageProvider'],
                                // loadingChild: loadingChild,
                                // backgroundDecoration: backgroundDecoration,
                                // minScale: true,
                                // maxScale: 1,
                                heroAttributes: PhotoViewHeroAttributes(tag: arguments['heroTag']),
                                enableRotation: false,      // 禁止旋转
                            ),
                        ),
                        // Positioned(//右上角关闭按钮
                        //     right: 10,
                        //     top: MediaQuery.of(context).padding.top,
                        //     child: IconButton(
                        //         icon: Icon(Icons.close,size: 30,color: Colors.white,),
                        //         onPressed: (){
                        //             Navigator.of(context).pop();
                        //         },
                        //     ),
                        // )
                    ],
                ),
            ),
            )
        );
    }

}

Video.dart展示视频
import 'package:flutter_ijkplayer/flutter_ijkplayer.dart';
import 'package:project/config/api_url.dart';
import 'package:project/plugins/ScreenAdapter.dart';
import 'package:flutter/material.dart';


class VideoApp extends StatefulWidget {
  var arguments;
  VideoApp(this.arguments);
  @override
  _VideoAppState createState() => _VideoAppState(this.arguments);
}

class _VideoAppState extends State<VideoApp> {
  var arguments;
  _VideoAppState(this.arguments);

  // // var aspet = 16/9;
  // VideoPlayerController _videoPlayerController;
  // ChewieController _chewieController;

  @override
  void initState() {
    super.initState();
    print('传进来的url:${arguments['message']}');
    _initVideo();
  }

   @override
    void dispose() async {
      // 停止
    // 这里要说明,ijkplayer的stop会释放资源,导致play不能使用,需要重新准备资源,所以这里其实采用的是回到进度条开始,并暂停
    await controller.stop();
    controller.dispose();
    super.dispose();
    }
  
  _initVideo() async {
    if(arguments['isMe']){
      await controller.setNetworkDataSource(
        arguments['message'],
        autoPlay: true
      );
    }else{
      await controller.setNetworkDataSource(
        '$base_url${arguments['message']}',
        autoPlay: true
      );
    }
  }


  IjkMediaController controller = IjkMediaController();

  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.black,
        centerTitle: false,
        leading: IconButton(
          icon: // Icon(IconData(0xe622, fontFamily: 'myIcon'),
                Icon(Icons.clear, color: Colors.white, size: ScreenAdapter.size(40),),
                onPressed: ()=>Navigator.pop(context),
                ),
      ),
      body: ConstrainedBox(
        constraints: BoxConstraints.expand(),
        child: Container(
                color: Colors.black,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  // padding: EdgeInsets.all(0),
                  children: <Widget>[
                    AspectRatio(
                      aspectRatio: 1,
                      child: IjkPlayer(
                        mediaController: controller,
                      ),
                    )
                  ]
                ),
              ),
      ),
    );
    
   
  }

  

}
完结了!所有的代码都在这里了,有什么问题一定给我留言,或者在群里找我
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章