您现在的位置是:网站首页> Flutter

Flutter实践问题及经验汇总

  • Flutter
  • 2026-08-01
  • 1971人已阅读
摘要

Flutter实践问题及经验汇总

*如有一个相当消耗时间的函数 test (int a,int b) 如何使用 Future 调用它*

1.jpg


重点问题汇总

创建一个Flutter项目后,打开MainActivity 出现Can't resolve symbol FlutterActivity 错误

国内网络环境开发 Flutter 如何优化开发环境



其他问题

Flutter中使用第三方库

Flutter开发中下载的第三方库所在位置

Flutter执行梳理和关键点整理

关于Flutter web字体下载出错

Flutter dart里根据不同的平台执行不同的代码

Flutter界面库

Flutter 的UI组件有哪些

Flutter所有布局组件的详细介绍及例子

Flutter发票图片寻边实现

Flutter列表的例子,每一项包含图片、图片说明以及删除、编辑和添加功能

Flutter的GridView.builder详细介绍下并给出例子



Flutter执行梳理和关键点整理

Dart基本语法

flutter如何判断平台执行不同的代码

执行过程

Flutter弹出新页面并获得新页面的返回值的例子

Flutter使用线程并使用线程同步的例子




Flutter中使用第三方库

1.在pubspec.yaml文件中添加依赖

在Flutter项目的pubspec.yaml文件中,在dependencies部分添加要使用的第三方库及其版本号。例如:

dependencies:

  flutter:

    sdk: flutter

  http: ^0.13.4

这里添加了http库的依赖,版本号为0.13.4。


2.运行flutter pub get命令

在终端或命令行中,进入Flutter项目的根目录,运行以下命令来获取添加的第三方库:

flutter pub get

这将根据pubspec.yaml文件中的依赖项下载并安装相应的第三方库。


3.在代码中导入和使用第三方库

在需要使用第三方库的Dart文件中,使用import语句导入库,然后就可以使用库提供的类、函数等功能了。例如:


import 'package:http/http.dart' as http;


void main() async {

  var response = await http.get(Uri.parse('https://api.example.com/data'));

  print(response.body);

}

这里导入了http库,并使用其提供的get函数发送HTTP GET请求,获取响应数据。


下面是一个使用第三方库的具体例子,演示了如何使用shared_preferences库来存储和读取简单的键值对数据:

1.在pubspec.yaml文件中添加shared_preferences依赖


dependencies:

  flutter:

    sdk: flutter

  shared_preferences: ^2.0.15


2.运行flutter pub get命令安装依赖。


3.在Dart文件中导入shared_preferences库


import 'package:shared_preferences/shared_preferences.dart';


4.使用shared_preferences库存储和读取数据:


void main() async {

  // 获取SharedPreferences实例

  SharedPreferences prefs = await SharedPreferences.getInstance();


  // 存储数据

  await prefs.setString('username', 'John');

  await prefs.setInt('age', 25);


  // 读取数据

  String username = prefs.getString('username') ?? '';

  int age = prefs.getInt('age') ?? 0;


  print('Username: $username');

  print('Age: $age');

}

在这个例子中,我们首先通过SharedPreferences.getInstance()获取SharedPreferences的实例。然后使用setString()和setInt()方法存储字符串和整数数据。最后,使用getString()和getInt()方法读取存储的数据,如果数据不存在,则使用空字符串和0作为默认值。

以上就是Flutter使用第三方库的方法和一个具体的例子。通过添加依赖、安装库和在代码中导入和使用库,我们可以方便地扩展Flutter应用的功能,提高开发效率。



Flutter开发中下载的第三方库所在位置

在 Flutter 开发中,下载的第三方库(Dart 包)的存储位置根据不同场景有所区别,主要分为以下几种情况:

1. 项目本地缓存(常用)

当你在项目中通过 pubspec.yaml 添加依赖并执行 flutter pub get 后,第三方库会被下载到项目的本地缓存目录,具体路径为:

<项目根目录>/.pub-cache/hosted/<仓库地址>/

例如:

使用官方 pub 仓库时:your_project/.pub-cache/hosted/pub.dartlang.org/

使用阿里云镜像时:your_project/.pub-cache/hosted/mirrors.aliyun.com_dart-pub/

这个目录下会按库名创建文件夹,存放对应版本的源代码。

2. 全局缓存(共享存储)

如果多个 Flutter 项目使用了相同的第三方库,为避免重复下载,Flutter 会将库缓存到全局目录,路径如下:

Windows:C:\Users\<你的用户名>\AppData\Roaming\Pub\Cache\hosted\

Mac/Linux:~/.pub-cache/hosted/

全局缓存与项目本地缓存的结构一致,当项目需要依赖时,会优先检查全局缓存,不存在时才会重新下载。

3. 特殊情况:本地路径依赖

如果在 pubspec.yaml 中通过本地路径引入依赖(如自定义库),则库的位置由你指定的路径决定,例如:

yaml

dependencies:

  my_local_package:

    path: ../my_local_package  # 相对路径或绝对路径

此时库的位置就是你指定的本地文件夹路径。

如何快速找到依赖位置?

1.在 Android Studio/VS Code 中,右键点击依赖库的导入语句(如 import 'package:dio/dio.dart';),选择 Go to Definition 或 Navigate to Source,会直接跳转到该库的源代码目录。

2.执行 flutter pub cache list 命令,可查看所有已缓存的依赖及其路径。

了解依赖的存储位置有助于调试第三方库源码、解决依赖冲突或手动修改临时需求(但不建议直接修改,最好通过 PR 向原库提交修改)。






flutter如何判断平台执行不同的代码

1. 使用 Platform 类

Flutter提供了一个 Platform 类,可以用来判断当前运行的平台。以下是一个简单的示例:

dart

import 'dart:io' show Platform;


void main() {

  if (Platform.isAndroid) {

    print('Running on Android');

  } else if (Platform.isIOS) {

    print('Running on iOS');

  } else if (Platform.isWindows) {

    print('Running on Windows');

  } else if (Platform.isLinux) {

    print('Running on Linux');

  } else if (Platform.isMacOS) {

    print('Running on macOS');

  } else {

    print('Running on an unknown platform');

  }

}


2. 使用 defaultTargetPlatform

defaultTargetPlatform 是 Flutter 提供的另一个方法,可以用来判断当前平台。以下是一个示例:

dart

import 'package:flutter/foundation.dart';


void main() {

  if (defaultTargetPlatform == TargetPlatform.android) {

    print('Running on Android');

  } else if (defaultTargetPlatform == TargetPlatform.iOS) {

    print('Running on iOS');

  } else if (defaultTargetPlatform == TargetPlatform.windows) {

    print('Running on Windows');

  } else if (defaultTargetPlatform == TargetPlatform.linux) {

    print('Running on Linux');

  } else if (defaultTargetPlatform == TargetPlatform.macOS) {

    print('Running on macOS');

  } else {

    print('Running on an unknown platform');

  }

}


3. 使用平台通道(Platform Channels)

对于需要调用平台特定的API,可以使用平台通道。以下是一个简单的示例,演示如何在Flutter中调用Android和iOS的电池电量API:

Dart代码:

dart

import 'package:flutter/services.dart';


class BatteryLevel {

  static const platform = MethodChannel('com.example.battery');


  Future<String> getBatteryLevel() async {

    try {

      final int result = await platform.invokeMethod('getBatteryLevel');

      return 'Battery level at $result % .';

    } on PlatformException catch (e) {

      return "Failed to get battery level: '${e.message}'.";

    }

  }

}


Android代码(Kotlin):

kotlin

import android.os.Bundle

import io.flutter.app.FlutterActivity

import io.flutter.plugin.common.MethodChannel


class MainActivity: FlutterActivity() {

  private val CHANNEL = "com.example.battery"


  override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->

      if (call.method == "getBatteryLevel") {

        val batteryLevel = getBatteryLevel()


        if (batteryLevel != -1) {

          result.success(batteryLevel)

        } else {

          result.error("UNAVAILABLE", "Battery level not available.", null)

        }

      } else {

        result.notImplemented()

      }

    }

  }


  private fun getBatteryLevel(): Int {

    val batteryLevel: Int

    val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager

    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)

    return batteryLevel

  }

}


iOS代码(Swift):

swift

import Flutter

import UIKit


@UIApplicationMain

@objc class AppDelegate: FlutterAppDelegate {

  override func application(

    _ application: UIApplication,

    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?

  ) -> Bool {

    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

    let batteryChannel = FlutterMethodChannel(name: "com.example.battery",

                                              binaryMessenger: controller.binaryMessenger)

    batteryChannel.setMethodCallHandler({

      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in

      if call.method == "getBatteryLevel" {

        self.receiveBatteryLevel(result: result)

      } else {

        result(FlutterMethodNotImplemented)

      }

    })


    return super.application(application, didFinishLaunchingWithOptions: launchOptions)

  }


  private func receiveBatteryLevel(result: FlutterResult) {

    let device = UIDevice.current

    device.isBatteryMonitoringEnabled = true

    if device.batteryState == UIDevice.BatteryState.unknown {

      result(FlutterError(code: "UNAVAILABLE",

                          message: "Battery level not available.",

                          details: nil))

    } else {

      result(Int(device.batteryLevel * 100))

    }

  }

}


通过这些方法,开发者可以在Flutter应用中根据不同的平台执行不同的代码,从而实现跨平台的功能。






执行过程

main函数入口,runApp启动

import 'package:flutter/material.dart';

import 'login_page.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Login Demo',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: LoginPage(),

    );

  }

}


LoginPage派生自StatefulWidget,里面实现产生状态类_LoginPageState,状态类传入LoginPage

import 'package:flutter/material.dart';


class LoginPage extends StatefulWidget {

  @override

  _LoginPageState createState() => _LoginPageState();

}


class _LoginPageState extends State<LoginPage> {

//这个类里有个变量widget就是LoginPage

  final _formKey = GlobalKey<FormState>();

  String _username = '';

  String _password = '';


  void _onSubmit() {

    if (_formKey.currentState!.validate()) {

      _formKey.currentState!.save();

      // 执行登录逻辑,比如发送网络请求等

      print('Username: $_username, Password: $_password');

    }

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Login'),

      ),

      body: Padding(

        padding: EdgeInsets.all(16.0),

        child: Form(

          key: _formKey,

          child: Column(

            children: [

              TextFormField(

                decoration: InputDecoration(labelText: 'Username'),

                validator: (value) {

                  if (value == null || value.isEmpty) {

                    return 'Please enter your username';

                  }

                  return null;

                },

                onSaved: (value) {

                  _username = value!;

                },

              ),

              TextFormField(

                decoration: InputDecoration(labelText: 'Password'),

                obscureText: true,

                validator: (value) {

                  if (value == null || value.isEmpty) {

                    return 'Please enter your password';

                  }

                  return null;

                },

                onSaved: (value) {

                  _password = value!;

                },

              ),

              SizedBox(height: 16.0),

              ElevatedButton(

                onPressed: _onSubmit,

                child: Text('Login'),

              ),

            ],

          ),

        ),

      ),

    );

  }

}



Flutter弹出新页面并获得新页面的返回值的例子

在Flutter中,你可以使用 Navigator.push() 方法弹出一个新页面,并通过 Navigator.pop() 方法返回数据给上一个页面。下面是一个示例:

1.首先,创建一个新页面 NewPage,并在其中添加一个文本字段和一个按钮:


class NewPage extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    final TextEditingController _textController = TextEditingController();


    return Scaffold(

      appBar: AppBar(

        title: Text('New Page'),

      ),

      body: Column(

        children: [

          TextField(

            controller: _textController,

            decoration: InputDecoration(

              labelText: 'Enter some text',

            ),

          ),

          ElevatedButton(

            onPressed: () {

              Navigator.pop(context, _textController.text);

            },

            child: Text('Return Data'),

          ),

        ],

      ),

    );

  }

}


2.在主页面中,添加一个按钮来打开新页面,并等待新页面返回数据:


class HomePage extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Home Page'),

      ),

      body: Center(

        child: ElevatedButton(

          onPressed: () async {

            final result = await Navigator.push(

              context,

              MaterialPageRoute(builder: (context) => NewPage()),

            );

            ScaffoldMessenger.of(context).showSnackBar(

              SnackBar(content: Text('Returned data: $result')),

            );

          },

          child: Text('Open New Page'),

        ),

      ),

    );

  }

}

在这个示例中:


在新页面 NewPage 中,我们创建了一个文本字段和一个按钮。当按钮被点击时,我们使用 Navigator.pop() 方法返回文本字段的内容给上一个页面。

在主页面 HomePage 中,我们添加了一个按钮来打开新页面。当按钮被点击时,我们使用 Navigator.push() 方法打开新页面,并等待新页面返回数据。

在新页面返回数据后,我们使用 ScaffoldMessenger 显示一个 SnackBar,以显示返回的数据。

通过这种方式,你可以在Flutter中弹出一个新页面,并获取新页面返回的数据。当新页面使用 Navigator.pop() 方法返回数据时,主页面可以通过 await 关键字等待并接收返回的数据


Flutter 页面跳转「传参过去 + 返回带结果」完整改进示例

两种场景全覆盖:

普通路由 MaterialPageRoute 跳转传参 + 返回值(最常用)

命名路由 pushNamed 传参 + 返回值

一、基础:普通路由(PageA → PageB,传参数过去,关闭带回结果)

1. 页面 A(发起跳转,携带参数传给 B,同时接收 B 返回的数据)

dart

import 'package:flutter/material.dart';


class PageA extends StatefulWidget {

  const PageA({super.key});


  @override

  State<PageA> createState() => _PageAState();

}


class _PageAState extends State<PageA> {

  // 保存页面B返回的数据

  String? resultText;


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: const Text("首页A")),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: [

            Text("页面返回结果:${resultText ?? '暂无'}"),

            const SizedBox(height: 30),

            ElevatedButton(

              onPressed: () async {

                // 1. 跳转时携带参数:name、age 传给 PageB

                final String? res = await Navigator.push(

                  context,

                  MaterialPageRoute(

                    builder: (ctx) => PageB(

                      name: "小明",

                      age: 18,

                    ),

                  ),

                );


                // 2. 接收页面B关闭时带回的返回值

                if (res != null) {

                  setState(() {

                    resultText = res;

                  });

                }

              },

              child: const Text("跳转并传参到B页面"),

            )

          ],

        ),

      ),

    );

  }

}

2. 页面 B(接收 A 传来的参数,操作后 pop 返回数据)

dart

class PageB extends StatelessWidget {

  // 接收上一页传来的参数

  final String name;

  final int age;

  const PageB({

    super.key,

    required this.name,

    required this.age,

  });


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: const Text("页面B(接收参数)")),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: [

            Text("接收A页面数据:姓名=$name,年龄=$age"),

            const SizedBox(height: 40),

            ElevatedButton(

              onPressed: () {

                // pop携带返回值,传回上一页

                Navigator.pop(context, "确认选中:$name,年龄$age");

              },

              child: const Text("确认,返回数据给A"),

            ),

            TextButton(

              onPressed: () {

                // 不带返回值,A页面拿到null

                Navigator.pop(context);

              },

              child: const Text("直接返回,无数据"),

            )

          ],

        ),

      ),

    );

  }

}

二、进阶:传递自定义对象(复杂参数)

1. 定义数据模型

dart

class UserInfo {

  final int id;

  final String username;

  final bool vip;

  UserInfo({

    required this.id,

    required this.username,

    required this.vip,

  });

}

2. A 页面跳转传对象

dart

ElevatedButton(

  onPressed: () async {

    // 构造对象传给B

    UserInfo sendUser = UserInfo(id: 1001, username: "李四", vip: true);

    final String? backMsg = await Navigator.push(

      context,

      MaterialPageRoute(builder: (_) => PageC(user: sendUser)),

    );

    if (backMsg != null) {

      setState(() {

        resultText = backMsg;

      });

    }

  },

  child: const Text("传递自定义对象跳转"),

)

3. C 页面接收对象并返回数据

dart

class PageC extends StatelessWidget {

  final UserInfo user;

  const PageC({super.key, required this.user});


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      body: Center(

        child: Column(

          children: [

            Text("ID:${user.id} 用户名:${user.username}"),

            ElevatedButton(

              onPressed: () {

                // 返回自定义字符串/对象都行

                Navigator.pop(context, "用户${user.username}操作完成");

              },

              child: const Text("提交返回"),

            )

          ],

        ),

      ),

    );

  }

}

三、命名路由 pushNamed 传参 + 返回值(项目常用)

1. 配置 MaterialApp

dart

MaterialApp(

  home: const PageA(),

  routes: {

    "/pageB": (ctx) => const PageB(name: "", age: 0),

  },

)

2. 跳转时通过 arguments 传参,同时接收返回值

dart

ElevatedButton(

  onPressed: () async {

    // arguments 传递参数

    final String? res = await Navigator.pushNamed(

      context,

      "/pageB",

      arguments: {"name": "小红", "age": 20},

    );

    if (res != null) setState(() => resultText = res);

  },

  child: const Text("命名路由跳转传参"),

)

3. PageB 内获取 arguments 参数

dart

class PageB extends StatelessWidget {

  const PageB({super.key, required String name, required int age});


  @override

  Widget build(BuildContext context) {

    // 获取路由传入的参数

    Map args = ModalRoute.of(context)!.settings.arguments as Map;

    String name = args["name"];

    int age = args["age"];


    return Scaffold(

      body: Center(

        child: ElevatedButton(

          onPressed: () => Navigator.pop(context, "命名路由返回:$name"),

          child: Text("姓名:$name"),

        ),

      ),

    );

  }

}

四、关键总结

普通路由传参:直接在页面构造函数传参,简单直观,适合绝大多数页面;

命名路由传参:使用 arguments,通过 ModalRoute.of(context)!.settings.arguments 读取;

返回值逻辑不变:Navigator.pop(context, 数据),上游通过 await 获取;

支持任意类型传递:字符串、数字、List、Map、自定义 Model 实体类都可以;

点击左上角返回 / 系统返回键时,返回值为 null,代码里做好判空。


PageB 是派生 StatefulWidget 呢

PageB 为 StatefulWidget 完整示例(跳转传参 + 返回数据)

逻辑流程:

PageA(Stateful)构造传参给 PageB

PageB 是 Stateful,接收入参,内部可修改状态

点击按钮 Navigator.pop(context, 返回数据) 回传给 PageA

1. 首页 PageA(发起跳转、传参、接收返回值)

dart

import 'package:flutter/material.dart';


void main() {

  runApp(const MyApp());

}


class MyApp extends StatelessWidget {

  const MyApp({super.key});


  @override

  Widget build(BuildContext context) {

    return MaterialApp(home: const PageA());

  }

}


class PageA extends StatefulWidget {

  const PageA({super.key});


  @override

  State<PageA> createState() => _PageAState();

}


class _PageAState extends State<PageA> {

  String? backResult; // 接收B页面返回的数据


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: const Text("A页面")),

      body: Center(

        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [

          Text("B页面返回结果:${backResult ?? '无'}"),

          const SizedBox(height: 30),

          ElevatedButton(

            onPressed: () async {

              // 跳转,构造函数传参 name、score

              String? res = await Navigator.push(

                context,

                MaterialPageRoute(

                  builder: (ctx) => PageB(name: "测试用户", score: 88),

                ),

              );

              // 拿到返回值刷新UI

              if (res != null) {

                setState(() {

                  backResult = res;

                });

              }

            },

            child: const Text("跳转到Stateful的PageB"),

          )

        ]),

      ),

    );

  }

}


2. PageB(StatefulWidget,接收参数、内部可变状态、返回数据)

dart

// Stateful 页面

class PageB extends StatefulWidget {

  // 接收A页面传来的参数,写在widget构造

  final String name;

  final int score;


  const PageB({

    super.key,

    required this.name,

    required this.score,

  });


  @override

  State<PageB> createState() => _PageBState();

}


class _PageBState extends State<PageB> {

  // 内部可变状态(Stateful特有)

  int localScore = 0;


  @override

  void initState() {

    super.initState();

    // 把传入的参数赋值给内部状态

    localScore = widget.score;

  }


  // 修改本地状态

  void addScore() {

    setState(() {

      localScore += 10;

    });

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(title: Text("B页面:${widget.name}")),

      body: Center(

        child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [

          Text("传入分数:${widget.score}"),

          Text("本地修改后分数:$localScore"),

          const SizedBox(height: 20),

          ElevatedButton(onPressed: addScore, child: const Text("分数+10")),

          const SizedBox(height: 20),

          // 携带数据返回上一页

          ElevatedButton(

            onPressed: () {

              String returnData = "用户:${widget.name},最终分数:$localScore";

              Navigator.pop(context, returnData);

            },

            child: const Text("确认返回,带回数据"),

          ),

          TextButton(

            onPressed: () {

              // 不带返回值

              Navigator.pop(context);

            },

            child: const Text("直接返回"),

          )

        ]),

      ),

    );

  }

}


关键知识点

1. Stateful 页面如何接收上级传参

参数定义在 PageB 类(Widget 层),用 final 修饰

State 中通过 widget.xxx 获取外部传入参数:widget.name / widget.score

不能直接在 State 构造传参,规范写法全部放在外层 Widget

2. 区分两种数据

外部传入参数:widget.name 不可变,页面销毁前固定

内部状态:localScore,通过 setState 动态修改 UI

3. 返回值逻辑不变

不管目标页面是 Stateless / Stateful,返回数据统一:

dart

Navigator.pop(context, 任意类型数据);

上游 await Navigator.push 接收 Future 返回值。

4. 传递自定义对象拓展

如果需要传复杂对象,只需要修改构造参数:

dart

class User {

  final int id;

  final String name;

  User({required this.id, required this.name});

}


// PageB 构造

const PageB({super.key, required this.user});

final User user;


// State中使用

Text(widget.user.name)




Flutter使用线程并使用线程同步的例子

Flutter 本身是单线程的,但它提供了 Isolate 机制来实现并发。以下是一个使用 Isolate 和线程同步的例子:

dart

import 'dart:isolate';

import 'package:flutter/foundation.dart';


void main() async {

  // 创建一个 ReceivePort 来接收消息

  final receivePort = ReceivePort();


  // 启动一个新的 Isolate

  await Isolate.spawn(heavyComputation, receivePort.sendPort);


  // 等待并打印结果

  final result = await receivePort.first;

  print('计算结果: $result');

}


// 在新的 Isolate 中执行的函数

void heavyComputation(SendPort sendPort) {

  int result = 0;

  

  // 模拟耗时计算

  for (int i = 0; i < 1000000000; i++) {

    result += i;

  }


  // 计算完成后,将结果发送回主 Isolate

  sendPort.send(result);

}


这个例子展示了如何:

使用 Isolate.spawn() 创建一个新的 Isolate 来执行耗时的计算任务.

使用 ReceivePort 和 SendPort 在主 Isolate 和新 Isolate 之间进行通信.

在主 Isolate 中等待计算结果并打印.


对于线程同步,Flutter 提供了一些机制:

Future 和 async/await: 用于处理异步操作.

Completer: 可以用来创建自定义的 Future.

Stream: 用于处理异步事件序列.

Mutex (在 synchronized 包中): 用于确保某段代码在同一时间只能被一个 Isolate 执行.

以下是一个使用 Mutex 的例子:

dart

import 'package:synchronized/synchronized.dart';


class SharedResource {

  final _lock = Lock();

  int _value = 0;


  Future<void> increment() async {

    await _lock.synchronized(() async {

      // 模拟一些耗时操作

      await Future.delayed(Duration(milliseconds: 100));

      _value++;

      print('当前值: $_value');

    });

  }

}


void main() async {

  final resource = SharedResource();

  

  // 模拟多个并发操作

  await Future.wait([

    resource.increment(),

    resource.increment(),

    resource.increment(),

  ]);

}


这个例子展示了如何使用 Lock 来确保 increment 方法在同一时间只能被一个调用者执行,从而避免竞态条件.

需要注意的是,虽然 Isolate 提供了并发能力,但它们之间不共享内存,通信只能通过消息传递。这种设计可以避免许多常见的并发问题,但也意味着在使用时需要特别注意数据的序列化和反序列化.



一个使用 Mutex 在 Flutter 中实现同步的例子:

import 'dart:async';

import 'package:flutter/material.dart';

import 'package:synchronized/synchronized.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Mutex Example',

      home: HomePage(),

    );

  }

}


class HomePage extends StatefulWidget {

  @override

  _HomePageState createState() => _HomePageState();

}


class _HomePageState extends State<HomePage> {

  int _counter = 0;

  final _mutex = Lock();

  Future<void> _incrementCounter() async {

    await _mutex.synchronized(() async {

      // 模拟一些异步操作

      await Future.delayed(Duration(seconds: 1));

      setState(() {

        _counter++;

      });

    });

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Mutex Example'),

      ),

      body: Center(

        child: Column(

          mainAxisAlignment: MainAxisAlignment.center,

          children: <Widget>[

            Text(

              'You have pushed the button this many times:',

            ),

            Text(

              '$_counter',

              style: Theme.of(context).textTheme.headline4,

            ),

          ],

        ),

      ),

      floatingActionButton: FloatingActionButton(

        onPressed: _incrementCounter,

        tooltip: 'Increment',

        child: Icon(Icons.add),

      ),

    );

  }

}

在这个例子中,我们使用了 synchronized 包中的 Lock 类来创建一个 Mutex 对象 _mutex。


在 _incrementCounter 方法中,我们使用 _mutex.synchronized 方法来包装需要同步的代码块。在这个代码块中,我们模拟了一些异步操作(使用 Future.delayed)并更新了计数器的值。

通过使用 Mutex,我们确保了在异步操作完成之前,其他并发调用 _incrementCounter 方法的代码不会进入同步代码块,从而避免了竞态条件和不一致的状态。

当你点击浮动操作按钮时,它会调用 _incrementCounter 方法,并在异步操作完成后更新计数器的值。由于使用了 Mutex,即使在快速连续点击按钮的情况下,计数器的值也会正确地递增,而不会出现竞态条件。

请注意,你需要将 synchronized 包添加到你的 pubspec.yaml 文件中,并运行 flutter pub get 来安装依赖项。

这就是一个在 Flutter 中使用 Mutex 实现同步的简单例子。Mutex 可以帮助你管理共享资源的访问,避免并发访问导致的问题。





关于Flutter web字体下载出错

修改项目目录下的pubspec.yaml文件添加字定义字体如

  assets:

    - assets/fonts/

  fonts:

   - family: CustomFont

     fonts:

       - asset: assets/fonts/site.ttf



1.png

代码修改

children: <Widget>[

            const Text(

              'You have pushed the button this many times:',

              style:  TextStyle(

                fontFamily: 'CustomFont',

              ),

            ),

            Text(

              '$_counter',

               style: const TextStyle(

                fontFamily: 'CustomFont',

              ),


            ),

          ],

点击下载例子

1.png

pubspec.yaml:

2.png

main.dart

3.png



Flutter dart里根据不同的平台执行不同的代码

使用 dart:io 库


import 'dart:io';


void executeBasedOnPlatform() {

  if (Platform.isAndroid) {

    // 执行 Android 特定的代码

    print("执行 Android 平台的代码");

  } else if (Platform.isIOS) {

    // 执行 iOS 特定的代码

    print("执行 iOS 平台的代码");

  } else if (Platform.isWindows) {

    // 执行 Windows 特定的代码

    print("执行 Windows 平台的代码");

  } else if (Platform.isMacOS) {

    // 执行 macOS 特定的代码

    print("执行 macOS 平台的代码");

  } else if (Platform.isLinux) {

    // 执行 Linux 特定的代码

    print("执行 Linux 平台的代码");

  }

}



使用 flutter/foundation.dart 库

对于 Flutter 特有的平台(如 Flutter Web),你可以使用 flutter/foundation.dart 库中的 kIsWeb 常量来判断是否是 Web 平台:


import 'package:flutter/foundation.dart' show kIsWeb;


void executeBasedOnPlatform() {

  if (kIsWeb) {

    // 执行 Web 特定的代码

    print("执行 Web 平台的代码");

  } else {

    // 执行非 Web 平台的代码

    print("执行非 Web 平台的代码");

  }

}



组合使用

在实际项目中,你可能需要根据多种情况判断并执行不同的代码。这时,你可以组合使用上述两种方法来实现更复杂的平台判断逻辑:


import 'dart:io';

import 'package:flutter/foundation.dart' show kIsWeb;


void executeBasedOnPlatform() {

  if (kIsWeb) {

    // 执行 Web 特定的代码

    print("执行 Web 平台的代码");

  } else if (Platform.isAndroid) {

    // 执行 Android 特定的代码

    print("执行 Android 平台的代码");

  } else if (Platform.isIOS) {

    // 执行 iOS 特定的代码

    print("执行 iOS 平台的代码");

  } else if (Platform.isWindows) {

    // 执行 Windows 特定的代码

    print("执行 Windows 平台的代码");

  } else if (Platform.isMacOS) {

    // 执行 macOS 特定的代码

    print("执行 macOS 平台的代码");

  } else if (Platform.isLinux) {

    // 执行 Linux 特定的代码

    print("执行 Linux 平台的代码");

  }

}



Flutter界面库

import  'package:flutter/material.dart'

void main(){

runApp(

 MaterialApp(

 title:'静态变量使用例子',

 home:MyApp()

)

)


class MyApp extends StatelessWidget{

@override

Widget build(BuildContent content){

return Scaffold(

appBar:AppBar(

title:Text(KString.mainTitle),

),

body:Center(

child:Text(KString.homeTitle,

         style:TextStyle(fontSize:28.0),

)

)

}

}


}


class KString{

static const String mainTitle="Flutter商城";

static const String homeTitle="首页";

}



Flutter所有布局组件的详细介绍及例子

Container

描述: 一个多功能的布局组件,可以添加padding、margin、边框等。

示例:

Container(

  margin: EdgeInsets.all(10),

  padding: EdgeInsets.all(8),

  decoration: BoxDecoration(

    color: Colors.blue,

    borderRadius: BorderRadius.circular(4),

  ),

  child: Text('Hello'),

)



Row

描述: 水平排列子组件。

示例:

Row(

  mainAxisAlignment: MainAxisAlignment.spaceEvenly,

  children: <Widget>[

    Icon(Icons.star),

    Icon(Icons.star),

    Icon(Icons.star),

  ],

)


Column

描述: 垂直排列子组件。

示例:

Column(

  crossAxisAlignment: CrossAxisAlignment.start,

  children: <Widget>[

    Text('Title'),

    Text('Subtitle'),

    Text('Body'),

  ],

)



Stack

描述: 允许子组件堆叠。

示例:

Stack(

  children: <Widget>[

    Container(color: Colors.yellow, width: 300, height: 300),

    Positioned(

      right: 20,

      top: 20,

      child: Icon(Icons.star, size: 50),

    ),

  ],

)


Expanded

描述: 在Row、Column或Flex中占用剩余空间。

示例:

Row(

  children: <Widget>[

    Container(width: 100, color: Colors.red),

    Expanded(

      child: Container(color: Colors.blue),

    ),

  ],

)


Flexible

描述: 类似Expanded,但可以设置flex参数控制占比。

示例:

Row(

  children: <Widget>[

    Flexible(

      flex: 2,

      child: Container(color: Colors.red),

    ),

    Flexible(

      flex: 1,

      child: Container(color: Colors.blue),

    ),

  ],

)


Wrap

描述: 当内容超出一行时自动换行。

示例:

Wrap(

  spacing: 8.0,

  runSpacing: 4.0,

  children: <Widget>[

    Chip(label: Text('Hamilton')),

    Chip(label: Text('Lafayette')),

    Chip(label: Text('Mulligan')),

    Chip(label: Text('Laurens')),

  ],

)


ListView

描述: 可滚动的列表。

示例:

ListView(

  children: <Widget>[

    ListTile(title: Text('Item 1')),

    ListTile(title: Text('Item 2')),

    ListTile(title: Text('Item 3')),

  ],

)


GridView

描述: 网格布局。

示例:

GridView.count(

  crossAxisCount: 2,

  children: List.generate(4, (index) {

    return Center(

      child: Text('Item $index'),

    );

  }),

)


SizedBox

描述: 指定固定大小的盒子。

示例:

SizedBox(

  width: 200,

  height: 100,

  child: Card(

    child: Text('Fixed Size Box'),

  ),

)


Padding

描述: 为其子组件添加内边距。

示例:

Padding(

  padding: EdgeInsets.all(16.0),

  child: Text('Hello, World!'),

)



Center

描述: 将子组件居中。

示例:

Center(

  child: Text('Centered Text'),

)


Align

描述: 根据设置的alignment参数对齐子组件。

示例:

Align(

  alignment: Alignment.topRight,

  child: Text('Top Right'),

)

这些是Flutter中最常用的布局组件。每个组件都有其特定的用途和属性,可以组合使用以创建复杂的布局。在实际开发中,你会经常使用这些组件的组合来构建用户界面。









Flutter 的UI组件有哪些

Flutter 提供了丰富的 UI 组件,可以帮助开发者快速构建美观、高性能的用户界面。以下是 Flutter 中常用的一些 UI 组件:


基础组件

Text:文本组件,用于显示文字内容。

Image:图片组件,用于显示图片。

Icon:图标组件,用于显示图标。

Button:按钮组件,包括 ElevatedButton、TextButton、OutlinedButton 等。


布局组件

Container:容器组件,可以设置背景色、边框、内边距等属性。

Row:行组件,用于水平排列子组件。

Column:列组件,用于垂直排列子组件。

Stack:堆叠组件,用于将子组件堆叠在一起。

Expanded:扩展组件,用于控制子组件在父组件中的尺寸。

Flexible

Flexible 组件与 Expanded 类似,用于控制子组件在 Row 或 Column 中的尺寸。

通过设置 Flexible 的 fit 属性,可以控制子组件在有限空间内的布局方式。

Flexible 组件常用于创建自适应的布局,如根据内容自动调整大小的文本框等。

Wrap

Wrap 组件用于在水平或垂直方向上依次排列子组件,当空间不足时会自动换行。

可以通过 direction、alignment、spacing 等属性来控制 Wrap 的布局行为。

Wrap 组件常用于创建流式布局,如标签云、动态网格等。




滚动组件

SingleChildScrollView:单子组件滚动视图,用于包裹单个可滚动的子组件。

ListView:列表组件,用于显示可滚动的列表。

GridView:网格组件,用于显示可滚动的网格列表。

CustomScrollView:自定义滚动视图,可以组合多个滚动组件。


表单组件

TextField:文本输入框组件,用于接收用户输入的文本。

Checkbox:复选框组件,用于多选。

Radio:单选框组件,用于单选。

Switch:开关组件,用于切换状态。

Slider:滑块组件,用于在指定范围内选择值。


对话框和弹出框

AlertDialog:警告对话框,用于显示警告信息。

SimpleDialog:简单对话框,用于显示简单的信息或选项。

BottomSheet:底部弹出框,从屏幕底部弹出的对话框。

SnackBar:消息提示框,在屏幕底部显示短暂的消息提示。


导航组件

Scaffold:脚手架组件,提供基本的页面结构,包括顶部应用栏、底部导航栏等。

AppBar:应用栏组件,通常放置在 Scaffold 的顶部,用于显示页面标题、导航按钮等。

TabBar 和 TabBarView:选项卡组件,用于实现页面的选项卡切换。

Drawer:抽屉组件,从左侧滑出的导航菜单。


其他组件

GestureDetector:手势检测器,用于处理各种手势事件,如点击、双击、拖动等。

AnimatedContainer:动画容器组件,可以实现容器属性的平滑过渡动画。

FutureBuilder 和 StreamBuilder:异步构建器组件,用于根据异步数据构建界面。

这只是 Flutter 中 UI 组件的一部分,Flutter 还提供了许多其他的组件和自定义组件的能力,可以满足各种复杂的 UI 设计需求。你可以参考 Flutter 的官方文档和示例来进一步了解和使用这些组件。



Flutter发票图片寻边实现

在 Flutter 中实现发票图片寻边可以使用图像处理库,如 OpenCV。以下是一个使用 OpenCV 实现发票图片寻边的示例:


添加依赖:在 pubspec.yaml 文件中添加 opencv 依赖:


dependencies:

  opencv: ^1.0.0

  image_picker: ^0.8.7+5 # 请确保使用最新的版本

导入必要的包:


import 'package:flutter/material.dart';

import 'package:image_picker/image_picker.dart';

import 'package:opencv/opencv.dart';

选择图片:


final picker = ImagePicker();

final pickedFile = await picker.getImage(source: ImageSource.gallery);

if (pickedFile != null) {

  final file = File(pickedFile.path);

  // 处理选择的图片

}

使用 OpenCV 进行图像处理:


final bytes = await file.readAsBytes();

final mat = await ImgProc.imdecode(bytes, ImgProc.IMREAD_GRAYSCALE);


// 高斯模糊

final blurredMat = await ImgProc.gaussianBlur(

  mat,

  Size(5, 5),

  0,

);


// Canny 边缘检测

final edges = await ImgProc.canny(

  blurredMat,

  50,

  150,

);


// 寻找轮廓

final contours = await ImgProc.findContours(

  edges,

  ImgProc.RETR_EXTERNAL,

  ImgProc.CHAIN_APPROX_SIMPLE,

);


// 找到最大的轮廓

final maxContour = contours.fold<List<Point>>(null, (prev, current) {

  if (prev == null || ImgProc.contourArea(current) > ImgProc.contourArea(prev)) {

    return current;

  }

  return prev;

});


// 绘制轮廓

final color = Colors.red;

final contourMat = await mat.clone();

await ImgProc.drawContours(

  contourMat,

  [maxContour],

  -1,

  color,

  2,

);


// 将处理后的图像转换为 Flutter 图像

final processedBytes = await ImgProc.imencode('.jpg', contourMat);

final processedImage = Image.memory(processedBytes);

显示处理后的图像:


Image(

  image: processedImage,

  fit: BoxFit.contain,

)

以上示例中,我们首先选择一张发票图片,然后使用 OpenCV 对图像进行处理。具体步骤包括:


将图像转换为灰度图像。

对图像进行高斯模糊,以减少噪声。

使用 Canny 算法进行边缘检测。

寻找图像中的轮廓。

找到最大的轮廓,假设它是发票的边框。

在原始图像上绘制最大轮廓。

将处理后的图像转换为 Flutter 图像并显示。

请注意,以上示例仅提供了一个基本的发票图片寻边实现。实际应用中,你可能需要进行更多的图像预处理和后处理,以获得更好的结果。



Flutter列表的例子,每一项包含图片、图片说明以及删除、编辑和添加功能

import 'package:flutter/material.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Flutter List Example',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: MyHomePage(),

    );

  }

}


class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}


class _MyHomePageState extends State<MyHomePage> {

  List<Map<String, dynamic>> items = [

    {'image': 'assets/image1.jpg', 'description': 'Image 1'},

    {'image': 'assets/image2.jpg', 'description': 'Image 2'},

    {'image': 'assets/image3.jpg', 'description': 'Image 3'},

  ];


  void _addItem() {

    setState(() {

      items.add({'image': 'assets/image4.jpg', 'description': 'New Image'});

    });

  }


  void _editItem(int index) {

    // 实现编辑功能

  }


  void _deleteItem(int index) {

    setState(() {

      items.removeAt(index);

    });

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Flutter List Example'),

      ),

      body: ListView.builder(

        itemCount: items.length,

        itemBuilder: (context, index) {

          return ListTile(

            leading: Image.asset(items[index]['image']),

            title: Text(items[index]['description']),

            trailing: Row(

              mainAxisSize: MainAxisSize.min,

              children: [

                IconButton(

                  icon: Icon(Icons.edit),

                  onPressed: () => _editItem(index),

                ),

                IconButton(

                  icon: Icon(Icons.delete),

                  onPressed: () => _deleteItem(index),

                ),

              ],

            ),

          );

        },

      ),

      floatingActionButton: FloatingActionButton(

        onPressed: _addItem,

        child: Icon(Icons.add),

      ),

    );

  }

}

在这个例子中,我们创建了一个MyHomePage类作为主页面,它继承自StatefulWidget。

在_MyHomePageState类中,我们定义了一个items列表,用于存储列表项的数据。每个列表项是一个包含image和description的Map。

我们使用ListView.builder构建列表,并在itemBuilder中为每个列表项创建一个ListTile。在ListTile中,我们使用Image.asset显示图片,并使用Text显示图片说明。同时,我们在trailing属性中添加了编辑和删除按钮。

点击编辑按钮会调用_editItem方法,传入当前列表项的索引。你可以在这个方法中实现编辑功能。

点击删除按钮会调用_deleteItem方法,传入当前列表项的索引。在这个方法中,我们使用removeAt方法从items列表中删除对应的项,并调用setState方法更新UI。

最后,我们添加了一个浮动操作按钮(FAB),点击它会调用_addItem方法,在该方法中,我们向items列表中添加一个新的项,并调用setState方法更新UI。


请注意,在上述例子中,我们还可以进一步优化和扩展功能。以下是一些建议:

使用自定义类来表示列表项数据:

我们可以创建一个单独的类来表示列表项的数据,而不是使用Map。这样可以使代码更加清晰和易于维护。例如:


class ListItem {

  final String image;

  final String description;


  ListItem({required this.image, required this.description});

}

然后,我们可以将items列表的类型更改为List<ListItem>。

实现编辑功能:

在_editItem方法中,我们可以弹出一个对话框或导航到一个新的页面,以允许用户编辑列表项的图片和描述。编辑完成后,我们可以更新items列表中对应项的数据,并调用setState方法更新UI。

持久化数据:

当前的例子中,列表项的数据是存储在内存中的,当应用程序关闭时,数据会丢失。我们可以使用数据持久化的方法,如SharedPreferences或SQLite,将数据保存到设备的存储中,以便在应用程序重新打开时恢复数据。

添加加载状态和空状态:

当列表项数据从网络或数据库中加载时,我们可以显示一个加载状态,如进度指示器或骨架屏幕,以提供更好的用户体验。同时,当列表为空时,我们可以显示一个空状态的视图,提示用户列表为空,并引导用户添加新的项。

使用动画和过渡效果:

为了增强用户体验,我们可以在列表项的添加、删除和编辑操作中使用动画和过渡效果。例如,在添加新项时使用渐变效果,在删除项时使用滑动删除效果等。Flutter提供了丰富的动画和过渡效果的API,如AnimatedList、SlideTransition等。

以上是一些可以进一步优化和扩展Flutter列表例子的建议。根据实际需求和应用场景,你可以选择适合的功能和改进方向。



编辑删除操作改成该项侧滑时出现

我们可以使用Flutter的Dismissible组件来实现列表项的侧滑操作,在侧滑时显示编辑和删除按钮。以下是修改后的代码:

import 'package:flutter/material.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Flutter List Example',

      theme: ThemeData(

        primarySwatch: Colors.blue,

      ),

      home: MyHomePage(),

    );

  }

}


class MyHomePage extends StatefulWidget {

  @override

  _MyHomePageState createState() => _MyHomePageState();

}


class _MyHomePageState extends State<MyHomePage> {

  List<Map<String, dynamic>> items = [

    {'image': 'assets/image1.jpg', 'description': 'Image 1'},

    {'image': 'assets/image2.jpg', 'description': 'Image 2'},

    {'image': 'assets/image3.jpg', 'description': 'Image 3'},

  ];


  void _addItem() {

    setState(() {

      items.add({'image': 'assets/image4.jpg', 'description': 'New Image'});

    });

  }


  void _editItem(int index) {

    // 实现编辑功能

  }


  void _deleteItem(int index) {

    setState(() {

      items.removeAt(index);

    });

  }


  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: Text('Flutter List Example'),

      ),

      body: ListView.builder(

        itemCount: items.length,

        itemBuilder: (context, index) {

          return Dismissible(

            key: Key(items[index]['description']),

            direction: DismissDirection.endToStart,

            onDismissed: (direction) {

              _deleteItem(index);

            },

            background: Container(

              alignment: Alignment.centerRight,

              padding: EdgeInsets.only(right: 20.0),

              color: Colors.red,

              child: Icon(Icons.delete, color: Colors.white),

            ),

            confirmDismiss: (direction) async {

              return await showDialog(

                context: context,

                builder: (BuildContext context) {

                  return AlertDialog(

                    title: Text("确认删除"),

                    content: Text("你确定要删除这个项吗?"),

                    actions: <Widget>[

                      TextButton(

                        child: Text("取消"),

                        onPressed: () {

                          Navigator.of(context).pop(false);

                        },

                      ),

                      TextButton(

                        child: Text("删除"),

                        onPressed: () {

                          Navigator.of(context).pop(true);

                        },

                      ),

                    ],

                  );

                },

              );

            },

            child: ListTile(

              leading: Image.asset(items[index]['image']),

              title: Text(items[index]['description']),

              trailing: IconButton(

                icon: Icon(Icons.edit),

                onPressed: () => _editItem(index),

              ),

            ),

          );

        },

      ),

      floatingActionButton: FloatingActionButton(

        onPressed: _addItem,

        child: Icon(Icons.add),

      ),

    );

  }

}

在修改后的代码中,我们使用Dismissible组件包裹每个列表项的ListTile。Dismissible组件允许我们通过侧滑手势来触发操作。


我们设置direction属性为DismissDirection.endToStart,表示只允许从右向左滑动。

在onDismissed回调中,我们调用_deleteItem方法来删除对应的列表项。

我们使用background属性来设置侧滑时显示的背景,这里我们设置了一个红色背景,并在右侧显示一个删除图标。

为了增加删除操作的确认步骤,我们使用confirmDismiss属性来显示一个确认对话框。当用户侧滑列表项时,会弹出一个对话框,询问用户是否确认删除。如果用户点击"删除"按钮,我们返回true,表示确认删除操作;如果用户点击"取消"按钮,我们返回false,表示取消删除操作。

对于编辑操作,我们将编辑按钮移动到ListTile的trailing属性中,当用户点击编辑按钮时,会调用_editItem方法,并传入对应的列表项索引。

这样修改后,列表项的删除操作就变成了侧滑触发,并且在删除前会显示一个确认对话框。编辑操作则通过点击列表项右侧的编辑按钮来触发。


你可以根据需要进一步自定义侧滑操作的样式和行为,例如添加更多的操作按钮、自定义滑动阈值等。

希望这个修改后的例子能够满足你的需求!如果有任何其他问题,欢迎随时提出。



Flutter的GridView.builder详细介绍下并给出例子

GridView.builder是 Flutter 中用于创建网格视图(grid view)的构建器。它允许你高效地构建一个二维的可滚动网格布局,适用于展示图片、图标、商品列表等以网格形式排列的内容。与ListView.builder类似,GridView.builder也是懒加载的,只有当网格中的子项进入屏幕可视范围时才会构建,这有助于提高性能和节省资源。

参数说明:

gridDelegate:这是一个必需的参数,用于定义网格的布局方式。最常用的是SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent。

SliverGridDelegateWithFixedCrossAxisCount:用于创建具有固定列数(对于垂直滚动的网格)或行数(对于水平滚动的网格)的网格布局。它的参数包括crossAxisCount(交叉轴方向上的子项数量,例如垂直滚动时的列数)、mainAxisSpacing(主轴方向上子项之间的间距)、crossAxisSpacing(交叉轴方向上子项之间的间距)和childAspectRatio(子项的宽高比)。

SliverGridDelegateWithMaxCrossAxisExtent:用于创建根据最大交叉轴范围自动计算列数或行数的网格布局。它的主要参数是maxCrossAxisExtent(交叉轴方向上子项的最大尺寸),同时也可以设置mainAxisSpacing、crossAxisSpacing和childAspectRatio。

itemCount:用于指定网格中总的子项数量,这是必需的参数。

itemBuilder:也是必需的参数,是一个构建器函数,用于为每个网格子项构建对应的组件。它接收BuildContext context和int index两个参数,其中context是构建上下文,index是当前子项的索引,通过index可以返回不同的组件来构建网格。

scrollDirection:指定网格的滚动方向,默认值是Axis.vertical(垂直滚动),也可以设置为Axis.horizontal(水平滚动)。

示例代码

以下是一个使用SliverGridDelegateWithFixedCrossAxisCount创建垂直滚动网格视图的例子,展示一个简单的包含 9 个彩色方块的网格:

import 'package:flutter/material.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: Scaffold(

        appBar: AppBar(

          title: Text('GridView.builder Example'),

        ),

        body: GridView.builder(

          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

            crossAxisCount: 3,

            mainAxisSpacing: 10,

            crossAxisSpacing: 10,

            childAspectRatio: 1.0,

          ),

          itemCount: 9,

          itemBuilder: (BuildContext context, int index) {

            return Container(

              color: Colors.blue[index * 100],

              child: Center(

                child: Text('Item $index'),

              ),

            );

          },

        ),

      ),

    );

  }

}

在这个示例中:

首先导入package:flutter/material.dart,这是使用 Flutter 的 Material Design 组件所必需的。

在MyApp类的build方法中,构建了一个MaterialApp,其中home属性是一个Scaffold。Scaffold包含一个AppBar用于显示标题,和一个body部分用于显示网格视图。

在body部分的GridView.builder中:

gridDelegate使用了SliverGridDelegateWithFixedCrossAxisCount,将crossAxisCount设置为 3,表示网格有 3 列;mainAxisSpacing和crossAxisSpacing都设置为 10,这意味着在主轴(垂直方向)和交叉轴(水平方向)上,子项之间的间距都是 10;childAspectRatio设置为 1.0,表示子项的宽高比为 1:1。

itemCount设置为 9,说明网格中总共有 9 个子项。

itemBuilder函数根据索引index返回一个Container作为网格子项。每个Container的颜色根据索引index设置(这里只是简单地通过Colors.blue[index * 100]来设置不同深浅的蓝色),并且在Container内部使用Center组件将一个Text组件居中显示,文本内容为Item $index。

如果要使用SliverGridDelegateWithMaxCrossAxisExtent创建一个水平滚动的网格视图,可以参考以下示例代码


import 'package:flutter/material.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: Scaffold(

        appBar: AppBar(

          title: Text('Horizontal GridView.builder Example'),

        ),

        body: Container(

          height: 150,

          child: GridView.builder(

            scrollDirection: Axis.horizontal,

            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(

              maxCrossAxisExtent: 100,

              mainAxisSpacing: 10,

              crossAxisSpacing: 10,

              childAspectRatio: 1.5,

            ),

            itemCount: 6,

            itemBuilder: (BuildContext context, int index) {

              return Container(

                decoration: BoxDecoration(

                  border: Border.all(color: Colors.black),

                ),

                child: Center(

                  child: Text('Item $index'),

                ),

              );

            },

          ),

        ),

      ),

    );

  }

}

在这个水平滚动网格视图示例中:

首先将GridView.builder放置在一个Container中,并设置了height为 150,用于限制网格视图的高度。

scrollDirection设置为Axis.horizontal,使网格可以水平滚动。

gridDelegate使用了SliverGridDelegateWithMaxCrossAxisExtent,将maxCrossAxisExtent设置为 100,表示交叉轴(垂直于滚动方向,在这里是高度方向)上子项的最大尺寸为 100;mainAxisSpacing设置为 10,crossAxisSpacing设置为 10,分别表示主轴(水平滚动方向)和交叉轴上子项之间的间距;childAspectRatio设置为 1.5,确定了子项的宽高比。

itemCount设置为 6,表示网格中有 6 个子项。

在itemBuilder函数中,返回一个带有黑色边框的Container作为网格子项,内部使用Center组件将一个Text组件居中显示,文本内容为Item $index。



创建一个Flutter项目后,打开MainActivity 出现Can't resolve symbol FlutterActivity 错误

废话不多,直接解决办法哈

删除项目中的android或ios文件夹,在项目的Terminal中运行

flutter create  .  以重新生成这些目录

but,出现一个问题,我原本想用java语言开发,使用这个命令直接生成Kotlin语言模式。。。。待解决更新

接着。。哈哈 

默认的是Kotlin语言,个人还是喜欢用java开发,使用:

flutter create -a java  .  命令即可生成java语言模式


当以下界面出现Module JDK is not时候设置一下JDK

1.png

直接以项目形式打开该Flutter项目下的Android目录

1.png

打开Flutter项目下的Android项目

1.png

Dart代码在Flutter主工程里编写,要改Android代码就在Android项目里,原生代码修改后需要在Android项目里编译

1.png

修改的 MainActivity.java代码:

package com.example.hiflutter;

import android.os.Bundle;

import android.os.Handler;

import android.app.AlertDialog;

import io.flutter.embedding.android.FlutterActivity;


public class MainActivity extends FlutterActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);


        // 使用 Handler 延迟显示,确保 Flutter 引擎初始化完成

        new Handler().postDelayed(new Runnable() {

            @Override

            public void run() {

                showStartupDialog();

            }

        }, 500); // 延迟 500ms

    }


    private void showStartupDialog() {

        // 在当前活动的上下文中创建对话框

        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setTitle("欢迎提示")

                .setMessage("应用程序已成功启动!")

                .setPositiveButton("确定", null) // 不需要点击事件

                .setCancelable(false); // 阻止用户通过返回键关闭


        // 创建并显示对话框

        AlertDialog dialog = builder.create();

        dialog.show();

    }

}

打开虚拟机

1.png


国内网络环境开发 Flutter 如何优化开发环境

Flutter 国内开发环境全套优化方案(解决超时、下载慢、编译卡、pub 失败、doctor 网络报错)

一、核心:配置国内镜像(根治 pub、SDK 下载超时)

1. 全局环境变量(永久生效,Windows/Mac/Linux 通用)

Windows(此电脑→属性→高级系统设置→环境变量,用户变量新增 3 项)

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

FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

FLUTTER_GIT_URL=https://gitee.com/mirrors/flutter.git

设置完成完全关闭所有终端再打开。

Mac/Linux

bash

# zsh用户

echo 'export PUB_HOSTED_URL=https://pub.flutter-io.cn' >> ~/.zshrc

echo 'export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn' >> ~/.zshrc

echo 'export FLUTTER_GIT_URL=https://gitee.com/mirrors/flutter.git' >> ~/.zshrc

source ~/.zshrc


# bash用户替换 ~/.bashrc

备用清华镜像(flutter-io 访问慢时切换)

plaintext

PUB_HOSTED_URL=https://mirrors.tuna.tsinghua.edu.cn/dart-pub

FLUTTER_STORAGE_BASE_URL=https://mirrors.tuna.tsinghua.edu.cn/flutter

2. Git Flutter 仓库替换(解决 flutter doctor GitHub 超时)

进入你的 Flutter SDK 目录

bash

cd D:/flutter

# 更换git远程源为gitee镜像

git remote set-url origin https://gitee.com/mirrors/flutter.git

# 验证

git remote -v

后续flutter upgrade、flutter doctor不再访问 GitHub。

二、Android SDK/gradle 国内加速(安卓编译 90% 卡顿根源)

1. Gradle 阿里云镜像(项目级 + 全局)

① 项目根目录 android/build.gradle

替换 repositories 块

gradle

repositories {

    // 注释掉 google()、mavenCentral() 原版

    maven { url 'https://maven.aliyun.com/repository/google' }

    maven { url 'https://maven.aliyun.com/repository/public' }

    maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }

}

② 全局 gradle 配置(一劳永逸)

用户目录 .gradle/init.gradle(没有就新建)

groovy

allprojects {

    repositories {

        def ALIYUN_REPOSITORY_URL = 'https://maven.aliyun.com/repository/public'

        def ALIYUN_GOOGLE_URL = 'https://maven.aliyun.com/repository/google'

        all { ArtifactRepository repo ->

            if (repo.url.toString().contains('google')) {

                remove repo

            }

            if (repo.url.toString().contains('mavenCentral')) {

                remove repo

            }

        }

        maven { url ALIYUN_GOOGLE_URL }

        maven { url ALIYUN_REPOSITORY_URL }

    }

}

2. Android SDK Manager 国内镜像

Android Studio → Tools → SDK Manager → SDK Update Sites

删除官方地址,添加:

https://dl.google.com/android → 替换为 https://dl.aliyun.com/android/android

https://dl.google.com/android/maven2 → 阿里云 maven

三、Git GitHub 访问优化(拉取 github 插件 / 源码超时)

方案 1:hosts 绑定 GitHub IP(无代理)

管理员打开 C:\Windows\System32\drivers\etc\hosts,末尾追加

plaintext

140.82.112.4 github.com

185.199.108.133 assets-cdn.github.com

199.232.68.133 github.global.ssl.fastly.net

刷新 DNS:

cmd

ipconfig /flushdns

方案 2:Git 全局代理(有加速器时)

bash

# 端口改成你的代理端口,如7890

git config --global http.proxy http://127.0.0.1:7890

git config --global https.proxy http://127.0.0.1:7890

# 取消代理

git config --global --unset http.proxy

git config --global --unset https.proxy

四、Flutter 命令缓存与下载优化

1. 清理损坏缓存(经常 pub get 失败执行)

bash

# 修复pub缓存

flutter pub cache repair

# 清空sdk下载缓存

rm -rf flutter/bin/cache

# 重新拉取依赖

flutter pub get

2. 禁用不必要的网络校验(消除 flutter doctor 警告)

flutter/packages/flutter_tools/lib/src/http_host_validator.dart

将校验 github.com、maven.google.com 的代码注释,或替换为国内镜像地址,彻底消除网络报错。

3. 依赖引入优先使用 Gitee / 本地路径

开发私有组件包,不要用 GitHub 地址,改用 Gitee 仓库依赖:

yaml

my_ui_lib:

  git:

    url: https://gitee.com/xxx/my_ui_lib.git

    tag: 1.0.0

五、编译 / 打包速度优化(国内网络 + 本地性能双重提速)

开启离线模式

bash

# 离线编译,不再请求网络依赖

flutter run --offline

flutter build apk --offline

开启 gradle 并行编译

android/gradle.properties 添加

properties

org.gradle.parallel=true

org.gradle.daemon=true

org.gradle.configureondemand=true

关闭 Android R8 混淆、缩减资源(开发调试)

build.gradle 关闭 minifyEnabled shrinkResources

六、VSCode/Android Studio 开发工具网络优化

VSCode Dart/Flutter 插件市场慢:使用国内镜像插件市场

Android Studio 代理配置:File→Settings→HTTP Proxy,填入本地加速器

禁用 IDE 自动在线检测 SDK 更新,避免后台网络请求拖慢开发

七、特殊场景:flutter upgrade 更新 SDK 加速

SDK 源已切换 Gitee 镜像后,执行:

bash

flutter upgrade

若还是慢:手动去 Gitee 镜像下载完整 SDK 压缩包替换本地 flutter 文件夹

八、避坑总结(高频国内网络问题)

flutter doctor 报 github 超时:更换 flutter git 远程源为 Gitee 镜像

flutter pub get 卡死:配置 PUB 国内镜像,清理 pub 缓存

Android 下载 gradle / 依赖半天不动:阿里云 maven 镜像

拉取 github 第三方包超时:hosts 绑定 IP 或换 Gitee 仓库

每次编译都联网下载:使用--offline离线运行

九、极简一键配置脚本(Windows PowerShell)

直接复制运行,自动配置镜像环境变量

powershell

[Environment]::SetEnvironmentVariable("PUB_HOSTED_URL", "https://pub.flutter-io.cn", "User")

[Environment]::SetEnvironmentVariable("FLUTTER_STORAGE_BASE_URL", "https://storage.flutter-io.cn", "User")

[Environment]::SetEnvironmentVariable("FLUTTER_GIT_URL", "https://gitee.com/mirrors/flutter.git", "User")

$env:PUB_HOSTED_URL="https://pub.flutter-io.cn"

$env:FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"

$env:FLUTTER_GIT_URL="https://gitee.com/mirrors/flutter.git"

echo "镜像环境变量配置完成,重启终端生效"




























Top