【flutter】搭建Android本地web服务项目开发流程详解

闲置不用的智能手机可以用来搭建本地web站点,托管静态网页,主要应用场景是把保存的电子资料放在本地web站点中供一家人(或者室友)访问,方便管理和查阅学习资料,还有其它自己想不到的应用场景
看懂此文章需要满足以下条件

  • 熟悉使用AndroidStudio开发工具
  • 会开发Flutter项目,了解插件相关知识
  • 会开发网页模板项目,了解部署静态网页托管
  1. 首先,AndroidStudio开发工具中,选择start a flutter project新建一个Flutter项目,其目录下有一个文件pubspec.yaml,将它打开后,自己加上如下四个插件,待添加好了,再点击一下右上角的蓝字Pub get让工具联网加载好以下插件
dependencies:
  flutter:
    sdk: flutter

  # 在这里对应下添加即可...
  dhttpd: ^4.0.1 #HTTP服务支持
  path_provider: ^2.0.9 #文件路径适配器
  ai_barcode: ^3.0.1 #二维码(条形码)
  url_launcher: ^6.1.0 #可打开访问链接
  1. 接下来,在项目路径下lib/main.dart文件中,修改代码如下
import 'dart:io';

import 'package:ai_barcode/ai_barcode.dart';
import 'package:dhttpd/dhttpd.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '本地web服务',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

/// 本地web服务,静态网页托管
class _MyHomePageState extends State<MyHomePage> {
	//...
}
  1. 重点看_MyHomePageState类这里,写好代码如下
/// 本地web服务,静态网页托管
class _MyHomePageState extends State<MyHomePage> {
  Dhttpd? _dpd;
  String _path = "";
  String _host = "";
  late CreatorController _creatorController;

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;//获取屏幕大小
    double vw = size.width < size.height ? size.width : size.height;//取最小的,例如屏宽度

    return Scaffold(
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Container(
                width: vw*0.6,//屏宽度的十分之六
                height: vw*0.6,
                //加边框
                decoration: const ShapeDecoration(
                  shape: RoundedRectangleBorder(
                    side: BorderSide(
                      color: Colors.black12,
                      width: 15,
                    ),
                    borderRadius: BorderRadius.all(
                      Radius.circular(10),
                    ),
                  ),
                ),
                //二维码组件
                child: PlatformAiBarcodeCreatorWidget(
                  creatorController: _creatorController,
                  initialValue: "请连接路由器局域网无AP隔离的WIFI",
                ),
                //加外边距
                margin: const EdgeInsets.fromLTRB(0, 50, 0, 20),
              ),
              const Text("手机浏览器扫此码即可访问"),
              const Divider(height: 1,),
              //站点服务的状态展示
              Padding(padding: const EdgeInsets.all(20), child:  Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text("站点地址: http://$_host"),
                  Text("存放路径: $_path"),
                ],
              ),),
            ],
          ),
        ),
      ),
      //打开链接地址访问
      floatingActionButton: FloatingActionButton(
        onPressed: _launchUrl,
        tooltip: 'View',
        child: const Icon(Icons.web),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _creatorController = CreatorController();
	//初始化本地Web服务
    _initLocalServer();
  }

  void _initLocalServer() async {
  	//获取内置sd卡存放目录
    var dir = await getExternalStorageDirectory();
    if (dir!=null) {
      var webDir = Directory("${dir.path}/www");
      if (!webDir.existsSync()) {
        webDir.createSync();//若没有www文件夹就创建一个
      }
      var webFile = File("${dir.path}/www/index.html");
      if (!webFile.existsSync()) {
        var html = await rootBundle.loadString("assets/www/index.html");
        webFile.writeAsStringSync(html);//若没有index.html网页文件就创建一个
      }
      String? _address;
		//遍历查找ip地址
      var interfaces = await NetworkInterface.list(includeLoopback: false, type: InternetAddressType.any);
      for (var interface in interfaces) {
        debugPrint("### name: ${interface.name} ");

        int i = 0;

        for (var address in interface.addresses) {
          debugPrint("${i++}. ${address.address}\n");
			//把找到的第一个ip地址赋值给_address
          _address ??= address.address;
        }

      }
		//如果有ip地址,就传给本地web服务器
      if (_address!=null) {
        _dpd = await Dhttpd.start(path: webDir.path, address: _address);
      }else {
        _dpd = await Dhttpd.start(path: webDir.path);
      }
		//更新状态显示
      setState(() {
        if (_dpd!=null) {
          _path = _dpd!.path;
          _host = "${_dpd!.host}:${_dpd!.port}";
        } else {
          _path = dir.path;
        }
      });
		//如果有ip地址,就重新生成二维码,方便其它手机扫码访问
      if (_address!=null){
        _creatorController.updateValue(value: "http://$_host");
      }

    }
  }
	//打开链接地址访问
  void _launchUrl() async {
    Uri _url = Uri.parse("http://$_host");
    if (!await launchUrl(_url)) throw 'Could not launch $_url';
  }
  
  @override
  void dispose() {
    // TODO: implement dispose
    if (_dpd!=null){
      _dpd!.destroy();//退出时不忘关闭服务器
    }
    super.dispose();
  }

}

小提示
代码中有个assets/www/index.html是网页文件,这里就不讲了,自己写网页放在项目中即可
如果还不熟悉,请直接看笔者TA远方上传的项目源码,直接编译运行

  1. 最后,附上运行效果图,项目源码在这里点击查看(建议用PC浏览器访问 ),在资源一栏可以找到,下载后解压,用AndroidStudio开发工具打开,直接编译运行

【flutter】搭建Android本地web服务项目开发流程详解_第1张图片
【flutter】搭建Android本地web服务项目开发流程详解_第2张图片
5.项目APP使用操作说明,新手必看

  1. 如果没有可用的WIFI路由器(默认开启了AP隔离,无法在局域网内互相访问),就在智能手机上设置打开WIFI热点,开启后,再运行此项目编译的APP,就能看到可以访问的IP地址
  2. 如果IP地址出现localhost, 127.0.0.1, 0.0.0.0,则这个除了本机能访问外,其它手机或电脑是不能访问到的
  3. 如果有自己开发的网页项目,就在手机上打开文件管理,在指定的存放路径文件夹www下,将里面的所有文件(包括index.html)替换为自己的即可

你可能感兴趣的:(Android,Flutter项目源码,android,flutter,静态网页托管,手机HTTP服务器,手机站点服务器)