linfeng 1 жил өмнө
parent
commit
d4bd8cb861

+ 101 - 35
lib/main.dart

@@ -1,7 +1,12 @@
+import 'dart:async';
+import 'dart:convert';
+
 import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
 import 'package:smartledz_wifi_test/plugin/wifi_plugin.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:smartledz_wifi_test/models/wifi_model.dart';
+import 'package:smartledz_wifi_test/utils/modal.dart';
 import 'package:smartledz_wifi_test/widgets/button_for_block.dart';
 import 'package:smartledz_wifi_test/widgets/wifi_item.dart';
 
@@ -44,28 +49,101 @@ class WifiList extends StatefulWidget {
 }
 
 class _WifiListState extends State<WifiList> {
-  final List<WifiModel> _wifiList = [
-    WifiModel(ssid: "zhonghui-5G", bssid: "1", rssi: 10),
-    WifiModel(ssid: "zhonghui_02", bssid: "2", rssi: 13),
-    WifiModel(ssid: "endo-lighting", bssid: "3", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "4", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "5", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "6", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "7", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "8", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "9", rssi: 50, result: "测试通过"),
-    WifiModel(ssid: "endo-lighting", bssid: "10", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "11", rssi: 50, result: "正在测试"),
-    WifiModel(ssid: "endo-lighting", bssid: "12", rssi: 50, result: "正在测试"),
-    WifiModel(ssid: "endo-lighting", bssid: "13", rssi: 50, result: "未通过"),
-    WifiModel(ssid: "endo-lighting", bssid: "14", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "15", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "16", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "17", rssi: 50),
-    WifiModel(ssid: "endo-lighting", bssid: "18", rssi: 50),
-  ];
 
+  final List<WifiModel> _wifiList = []; // 扫描的wifi列表
   final List<String> _selectWifiList = []; // 已选择的wifi的bssid标识列表
+  late bool testing = false; // 是否测试中:false[否]、true[是]
+
+  @override
+  void initState() {
+    super.initState();
+
+    var eventChannel = const EventChannel('native_event_channel');
+    eventChannel.receiveBroadcastStream().listen((dataStr) {
+      Map jsonArr = jsonDecode(dataStr);
+      debugPrint("收到通知事件 ---> $jsonArr");
+      switch(jsonArr["cmd"]){
+        case "scanResult": // 扫描
+          setState(() {
+            _wifiList.add(WifiModel(
+                ssid: jsonArr["ssid"],
+                bssid: jsonArr["bssid"],
+                rssi: int.parse(jsonArr["level"])
+            ));
+          });
+          break;
+
+        case "scanComplete": // 扫描完成
+          setState(() { // 检查已选列表的wifi是否真实存在
+            _selectWifiList.removeWhere((bssid){
+              for(WifiModel wifiModel in _wifiList){ // 已存在则不删除
+                if(wifiModel.bssid == bssid) return false;
+              }
+              return true;
+            });
+          });
+          break;
+      }
+    });
+
+    startScanWifi();
+    Timer.periodic(const Duration(seconds: 3), (timer) {
+      if(testing) return; // 测试中,忽略定时扫描
+      startScanWifi(); // 定时调用扫描方法
+    });
+  }
+
+  // 扫描wifi
+  void startScanWifi(){
+    _wifiList.clear();
+    WiFiPlugin.startScan();
+  }
+
+  // 开始测试
+  void startTest(){
+    if(_wifiList.isEmpty){
+      Modal.toast(msg: "wifi列表为空,无法测试");
+      return;
+    }
+
+    testing = true; // 置为测试状态
+    if(_selectWifiList.isNotEmpty){ // 有选中的wifi,则只处理选中的wifi
+      eachTestWifiList(true);
+      return;
+    }
+
+    // 处理全部wifi
+    eachTestWifiList(false);
+  }
+
+  // 循环测试wifi列表
+  void eachTestWifiList(bool isSelect, [int index=0]){
+    int wifiIndex = index;
+    if(isSelect == true){ // 处理选中设备
+      if(index >= _selectWifiList.length){ // 处理完成
+        testing = false;
+        return;
+      }
+      for(int i=0; i<_wifiList.length; i++){
+        if(_wifiList[i].bssid == _selectWifiList[index]){
+          wifiIndex = i; // 获取选中设备在wifi列表中的索引值
+          return;
+        }
+      }
+    }else{
+      if(index >= _wifiList.length){ // 处理完成
+        testing = false;
+        return;
+      }
+    }
+
+    WifiModel wifiModel = _wifiList[wifiIndex];
+
+    setState(()=>_wifiList[wifiIndex].result="正在测试");
+    debugPrint("开始连接wifi  ssid --> ${wifiModel.ssid}   bssid --> ${wifiModel.bssid}");
+    WiFiPlugin.connect(wifiModel.ssid, wifiModel.bssid, "12345678");
+    testing = false; // TODO developer,置为非测试状态
+  }
 
   @override
   Widget build(BuildContext context) {
@@ -93,23 +171,11 @@ class _WifiListState extends State<WifiList> {
         ),
         ButtonForBlock(
           title: "开始测试",
+          fontSize: 20.sp,
           radius: 0,
           width: MediaQuery.of(context).size.width,
-          height: 50.sp,
-          onTap: (){
-            if(_selectWifiList.isNotEmpty){
-              // 有选中的wifi,则只处理选中的wifi
-
-              // TODO 此处完善处理逻辑
-              // 选中的wifi bssid列表 _selectWifiList
-
-              return;
-            }
-
-            // 处理全部wifi
-            // TODO 此处完善处理逻辑
-
-          },
+          height: 55.sp,
+          onTap: startTest,
         )
       ],
     );

+ 201 - 0
lib/utils/modal.dart

@@ -0,0 +1,201 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+import 'package:smartledz_wifi_test/widgets/animations/animation_for_rotation.dart';
+
+class Modal {
+
+  // 构建模态框标题
+  static Widget buildTitle({
+    required String title,
+    double? size,
+  }){
+    return Padding(
+      padding: EdgeInsets.only(bottom: 5.sp),
+      child: Center(
+        child: Text(
+          title,
+          style: TextStyle(
+            fontWeight: FontWeight.bold,
+            fontSize: size ?? 15.sp,
+            color: Colors.black,
+          )
+        ),
+      ),
+    );
+  }
+
+  // 构建文字内容
+  static Widget buildContent({
+    required String content,
+    TextAlign? textAlign,
+  }){
+    return Text(
+      content,
+      textAlign: textAlign,
+      style: TextStyle(
+        fontSize: 15.sp,
+        color: const Color(0xAA000000)
+      )
+    );
+  }
+
+  // 构建带边框的输入框
+  static Widget buildInputForBorder({
+    String? initValue,
+    String? hintText,
+    List<TextInputFormatter>? inputFormatters,
+    TextEditingController? controller,
+    void Function(String)? onChanged,
+  }){
+    return Container(
+      height: 40.sp,
+      margin: EdgeInsets.symmetric(vertical: 15.sp),
+      decoration: BoxDecoration(
+        border: Border.all(
+          color: const Color(0xAA000000),
+          width: .5
+        )
+      ),
+      child: TextFormField(
+        controller: controller,
+        initialValue: initValue,
+        onChanged: onChanged,
+        cursorColor: const Color(0xFF00AAFF),
+        decoration: InputDecoration(
+          hintText: hintText,
+          hintStyle: TextStyle(
+            fontSize: 15.sp,
+            color: Colors.grey.shade500
+          ),
+          border: const OutlineInputBorder(borderSide: BorderSide.none),
+          contentPadding: EdgeInsets.symmetric(horizontal: 10.sp)
+        ),
+        inputFormatters: inputFormatters,
+        style: TextStyle(fontSize: 15.sp),
+      ),
+    );
+  }
+
+  // 构建两个一组的按钮
+  static Widget buildButtonForTwo({
+    required String leftBtnTitle,
+    required String rightBtnTitle,
+    void Function()? onLeftBtnTap,
+    void Function()? onRightBtnTap,
+  }){
+    return Row(
+      children: [
+        GestureDetector(
+          onTap: onLeftBtnTap,
+          child: Container(
+            color: const Color(0xFFAAAAAA),
+            padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.w),
+            child: Text(leftBtnTitle, style: TextStyle(color: Colors.white, fontSize: 15.sp)),
+          ),
+        ),
+        Expanded(
+          child: Align(
+            alignment: Alignment.centerRight,
+            child: GestureDetector(
+              onTap: onRightBtnTap,
+              child: Container(
+                color: const Color(0xFF00AAFF),
+                padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.w),
+                child: Text(rightBtnTitle, style: TextStyle(color: Colors.white, fontSize: 15.sp)
+                ),
+              ),
+            ),
+          ),
+        )
+      ],
+    );
+  }
+
+  // 构建自撑宽度的按钮
+  static Widget buildButtonForFull({
+    required String title,
+    void Function()? onTap,
+  }){
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        alignment: Alignment.center,
+        color: const Color(0xFF00AAFF),
+        padding: EdgeInsets.symmetric(vertical: 8.h),
+        child: Text(title, style: TextStyle(color: Colors.white, fontSize: 15.sp)
+        ),
+      ),
+    );
+  }
+
+  // 打开并显示模态框
+  static void open({
+    required BuildContext context,
+    required Widget Function(BuildContext) builder,
+    double? width,
+    EdgeInsetsGeometry? padding,
+    bool disableShadeClose = false, // 禁用遮罩点击关闭
+    bool disableReturnKey = false, // 禁用返回键
+  }){
+    showDialog(
+      barrierDismissible: !disableShadeClose,
+      context: context,
+      builder: (BuildContext context){
+        Widget child = UnconstrainedBox(
+          child: Container(
+            width: width ?? 300.w,
+            color: Colors.white,
+            padding: padding ?? EdgeInsets.all(15.w),
+            child: builder(context),
+          ),
+        );
+        if(disableReturnKey == true){
+          return WillPopScope(child: child, onWillPop: () async => false);
+        }
+        return child;
+      }
+    );
+  }
+
+  // 打开加载遮罩层
+  static void openLoading({
+    required BuildContext context,
+    required String tip,
+  }){
+    showDialog(
+      barrierDismissible: false,
+      context: context,
+      builder: (BuildContext context){
+        return WillPopScope(
+          onWillPop: () async => false,
+          child: UnconstrainedBox(
+            child: Row(
+              children: [
+                AnimationForRotation(
+                  duration: const Duration(milliseconds: 400),
+                  child: Icon(Icons.refresh, color: const Color(0xFF00AAFF), size: 45.sp),
+                ),
+                SizedBox(width: 10.sp),
+                Text(tip, style: TextStyle(color: Colors.white, fontSize: 23.sp, fontWeight: FontWeight.bold))
+              ],
+            ),
+          ),
+        );
+      }
+    );
+  }
+
+  // Toast消息提示
+  static void toast({
+    required String msg
+  }){
+    Fluttertoast.showToast(
+      msg: msg,
+      gravity: ToastGravity.TOP,
+      backgroundColor: const Color(0xFFEEEEEE),
+      textColor: const Color(0xFF00AAFF)
+    );
+  }
+}

+ 54 - 0
lib/widgets/animations/animation_for_rotation.dart

@@ -0,0 +1,54 @@
+import 'package:flutter/material.dart';
+
+// 旋转动画
+class AnimationForRotation extends StatefulWidget {
+  final Duration duration;
+  final bool play;
+  final Widget child;
+
+  const AnimationForRotation({
+    Key? key,
+    required this.duration,
+    required this.child,
+    this.play = true,
+  }) : super(key: key);
+
+  @override
+  State<AnimationForRotation> createState() => _AnimationForRotation();
+}
+
+class _AnimationForRotation extends State<AnimationForRotation> with SingleTickerProviderStateMixin {
+  late AnimationController _controller;
+  late bool isStop = false;
+
+  @override
+  void dispose() {
+    // Widget销毁时,必须销毁动画
+    _controller.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    _controller = AnimationController(duration: const Duration(milliseconds: 600), vsync: this);
+    _controller.repeat();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if(widget.play == false){ // 动画暂停
+      setState(()=>isStop = true);
+      _controller.stop();
+    }else if(isStop == true){ // 继续动画
+      setState(()=>isStop = false);
+      _controller.repeat();
+    }
+
+    return RotationTransition(
+      alignment: Alignment.center,
+      turns: _controller,
+      child: widget.child,
+    );
+  }
+}

+ 6 - 5
lib/widgets/wifi_item.dart

@@ -31,7 +31,8 @@ class _WifiItemState extends State<WifiItem> {
   @override
   Widget build(BuildContext context) {
 
-    Color resultColor = Colors.grey;
+    double borderWidth = .7.sp; // 边框宽度
+    Color resultColor = Colors.grey; // 测试结果文本颜色
     if(resultAsColor.containsKey(widget.wifiModel.result)){
       resultColor = resultAsColor[widget.wifiModel.result]!;
     }
@@ -39,11 +40,11 @@ class _WifiItemState extends State<WifiItem> {
     return GestureDetector(
       onTap: ()=>widget.onTap?.call(widget.wifiModel),
       child: Container(
-        height: 40.sp,
+        height: 50.sp,
         decoration: BoxDecoration(
           color: widget.active? Colors.pink.shade100: null,
           border: Border(
-            bottom: BorderSide(color: Colors.grey, width: .5.sp)
+            bottom: BorderSide(color: Colors.grey, width: borderWidth)
           )
         ),
         child: Row(
@@ -52,14 +53,14 @@ class _WifiItemState extends State<WifiItem> {
               child: Container(
                 alignment: Alignment.centerLeft,
                 padding: EdgeInsets.symmetric(horizontal: 5.sp),
-                child: Text(widget.wifiModel.ssid, style: TextStyle(fontSize: 16.sp)),
+                child: Text(widget.wifiModel.ssid, style: TextStyle(fontSize: 18.sp)),
               ),
             ),
             Container(
               alignment: Alignment.center,
               width: 50.w,
               decoration: BoxDecoration(
-                border: Border.symmetric(vertical: BorderSide(color: Colors.grey, width: .5.sp))
+                border: Border.symmetric(vertical: BorderSide(color: Colors.grey, width: borderWidth))
               ),
               child: Text(widget.wifiModel.rssi.toString(), style: TextStyle(fontSize: 14.sp, color: Colors.green)),
             ),

+ 13 - 0
pubspec.lock

@@ -83,6 +83,19 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_plugins:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
+  fluttertoast:
+    dependency: "direct main"
+    description:
+      name: fluttertoast
+      sha256: "474f7d506230897a3cd28c965ec21c5328ae5605fc9c400cd330e9e9d6ac175c"
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "8.2.2"
   js:
     dependency: transitive
     description:

+ 1 - 0
pubspec.yaml

@@ -37,6 +37,7 @@ dependencies:
   cupertino_icons: ^1.0.2
   permission_handler: ^10.3.0
   flutter_screenutil: ^5.7.0
+  fluttertoast: ^8.2.2
 
 dev_dependencies:
   flutter_test: