目录
解决环境报错 zsh:command not found:flutter
通过 sudo chmod -R 777 * 修改一下文件权限
一、参考文档
API文档 |
链接 |
---|---|
Flutter地址 |
|
Homebrew地址 |
|
pub包管理系统 |
|
Material Design官方Icons图标 |
二、准备工作
升级Macos系统为最新系统
安装最新的Xcode
电脑上面需要安装brew https://brew.sh/
安装chrome浏览器(开发web用)
下载Flutter SDK:
FlutterSDK下载地址: https://docs.flutter.dev/get-started/install
常用编译软件:
Android Studio with the Flutter plugin for IntelliJ.
IntelliJ IDEA with both the Flutter plugin for IntelliJ and the Android plugin for IntelliJ.
下载zip并解压到本地文件中
配置环境
打开命令行,执行【open ~/.bash_profile 】
把刚解压好的FlutterSDK文件地址进行配置,把内容粘贴到.bash_profile文件
export PATH="$PATH:/home/yourusername/flutter/bin"
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
3. 执行【source ~/.bash_profile 】,将.bash_profile文件生效
4. 执行【flutter doctor】测试效果。
注意哈:这样配置,如果你只有(bash_profile,没有zshrc文件)。
需要每次打开终端输入一下 source ~/.bash_profile 命令,再使用flutter命令。不然会报错。
zsh:command not found:flutter
像下面图片效果:
解决环境报错 zsh:command not found:flutter
1、执行:【open ~/.zshrc 】
2、如果 提示文件不存在,则执行:【vim ~/.zshrc 】新建一个新文件。
vim ~/.zshrc
再使用source命令重新加载一下:【source ~/.zshrc】,下次再编辑这个文件就可以直接执行:【open ~/.zshrc】
source ~/.zshrc
执行【flutter doctor】测试效果
这个时候试试关闭终端再输入flutter doctor,此时Flutter SDK配置完成,按照下面提示进行安装操作(下载Android SDK、Xcode、CocoaPods)。
安装Xcode IOS环境
需要安装brew,通过brew安装CocoaPods.
Homebrew官网: https://brew.sh/
复制命令行,打开终端
分别执行下面命令
brew install cocoapods.
pod setup
sudo xcode-select --switch /Applications/xcode.app/contents/Developer
sudo xcodebuild -runFirstLaunch
如图所示
brew doctor
三、创建Flutter IOS项目
Mac 创建Flutter IOS项目
sudo flutter create flutterdemo
sudo chmod -R 777 flutterdemo //修改一下文件权限 可读。可写
通过 sudo chmod -R 777 * 修改一下文件权限
Xcode 打开Flutter项目
flutter run
flutter -d all
flutter -d chrome
Android Studio创建Flutter项目
Flutter目录层级介绍
依赖库/图片的引用
pub包管理系统:
官网地址:https://pub.dev/
使用第三库,在官网找到对应的库
复制地方库的引用方式
四、Flutter简单应用
1.主题设置
在 Flutter 项目里,实现主题切换功能需要结合状态管理和 Flutter 的主题系统。下面为你介绍实现主题切换的具体步骤:
1.1 定义主题数据
要先创建亮、暗两种主题,并且设置好各自的颜色和样式。
import 'package:flutter/material.dart';
class AppThemes {
static final lightTheme = ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
scaffoldBackgroundColor: Colors.white,
// 其他主题属性...
);
static final darkTheme = ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.blue,
scaffoldBackgroundColor: Colors.black,
// 其他主题属性...
);
}
1.2 状态管理(以 Provider 为例)
接着创建一个主题状态管理类,以此来保存和更新当前使用的主题。
import 'package:flutter/material.dart';
class ThemeProvider with ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
void toggleTheme(bool isDark) {
_themeMode = isDark ? ThemeMode.dark : ThemeMode.light;
notifyListeners();
}
void setSystemTheme() {
_themeMode = ThemeMode.system;
notifyListeners();
}
}
1.3 在 MaterialApp 中应用主题
然后在应用的根 Widget 里配置主题,并监听主题变化。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => ThemeProvider(),
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<ThemeProvider>(
builder: (context, themeProvider, child) {
return MaterialApp(
title: 'Flutter Theme Demo',
theme: AppThemes.lightTheme,
darkTheme: AppThemes.darkTheme,
themeMode: themeProvider.themeMode,
home: const HomePage(),
);
},
);
}
}
1.4 创建主题切换界面
最后添加一个用于切换主题的 UI 组件,像开关或按钮。
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context);
final isDark = themeProvider.themeMode == ThemeMode.dark;
return Scaffold(
appBar: AppBar(
title: const Text('主题切换示例'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('切换主题'),
Switch(
value: isDark,
onChanged: (value) {
themeProvider.toggleTheme(value);
},
),
],
),
),
);
}
}
2. 国际化
在 Flutter 项目中设置国际化需要配置多语言资源并确保应用能根据系统语言自动切换。以下是实现步骤:
2.1 添加依赖
在pubspec.yaml
中添加flutter_localizations
和intl
插件:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.18.1 # 最新版本
2.2 配置支持的语言
在MaterialApp
中指定支持的语言列表和本地化代理:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.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: 'Flutter 国际化示例',
// 支持的语言列表
supportedLocales: [
Locale('en', 'US'), // 英语
Locale('zh', 'CN'), // 中文
// 添加更多语言...
],
// 本地化代理
localizationsDelegates: [
AppLocalizations.delegate, // 自定义代理
GlobalMaterialLocalizations.delegate, // Material组件本地化
GlobalWidgetsLocalizations.delegate, // 小部件库本地化
GlobalCupertinoLocalizations.delegate, // Cupertino组件本地化
],
// 根据系统语言自动选择locale
localeResolutionCallback: (locale, supportedLocales) {
for (var supportedLocale in supportedLocales) {
if (supportedLocale.languageCode == locale?.languageCode) {
return supportedLocale;
}
}
return supportedLocales.first; // 默认返回第一个支持的语言
},
home: const HomePage(),
);
}
}
2.3 创建多语言资源文件
创建一个抽象类定义所有翻译键,并为每种语言创建实现类:
定义基础接口
// lib/l10n/app_localizations.dart
import 'package:flutter/material.dart';
abstract class AppLocalizations {
static AppLocalizations of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
}
static const LocalizationsDelegate<AppLocalizations> delegate = _AppLocalizationsDelegate();
// 定义翻译键
String get appTitle;
String greeting(String name);
// 添加更多翻译...
}
实现英语资源
// lib/l10n/app_localizations_en.dart
class AppLocalizationsEn extends AppLocalizations {
@override
String get appTitle => 'Flutter Internationalization';
@override
String greeting(String name) => 'Hello, $name!';
}
实现中文资源
// lib/l10n/app_localizations_zh.dart
class AppLocalizationsZh extends AppLocalizations {
@override
String get appTitle => 'Flutter 国际化';
@override
String greeting(String name) => '你好,$name!';
}
创建本地化代理
// lib/l10n/app_localizations.dart (继续)
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);
@override
Future<AppLocalizations> load(Locale locale) async {
switch (locale.languageCode) {
case 'en':
return AppLocalizationsEn();
case 'zh':
return AppLocalizationsZh();
default:
return AppLocalizationsEn(); // 默认使用英语
}
}
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
在 UI 中使用本地化字符串
// lib/home_page.dart
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final localizations = AppLocalizations.of(context);
return Scaffold(
appBar: AppBar(
title: Text(localizations.appTitle),
),
body: Center(
child: Text(localizations.greeting('World')),
),
);
}
}
3. 相机相册调用
在 Flutter 中调用相机和相册功能,需要使用image_picker
插件。以下是完整实现步骤:
3.1 添加依赖
在pubspec.yaml
中添加依赖:
dependencies:
flutter:
sdk: flutter
image_picker: ^1.0.2 # 最新版本
然后执行flutter pub get
安装。
3.2 配置权限
iOS
在ios/Runner/Info.plist
中添加:
<key>NSCameraUsageDescription</key>
<string>应用需要访问相机来拍摄照片</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>应用需要访问相册来选择照片</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>应用需要保存照片到相册</string>
Android
在android/app/src/main/AndroidManifest.xml
中添加:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Android 10及以下需要 -->
3.3 实现相机和相册功能
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
class CameraGalleryExample extends StatefulWidget {
@override
_CameraGalleryExampleState createState() => _CameraGalleryExampleState();
}
class _CameraGalleryExampleState extends State<CameraGalleryExample> {
final ImagePicker _picker = ImagePicker();
XFile? _pickedImage;
// 从相机拍摄照片
Future<void> _takePhoto() async {
try {
final XFile? photo = await _picker.pickImage(source: ImageSource.camera);
if (photo != null) {
setState(() {
_pickedImage = photo;
});
}
} catch (e) {
print('Error taking photo: $e');
// 显示错误提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('拍照失败: $e')),
);
}
}
// 从相册选择照片
Future<void> _selectFromGallery() async {
try {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
setState(() {
_pickedImage = image;
});
}
} catch (e) {
print('Error selecting image: $e');
// 显示错误提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('选择图片失败: $e')),
);
}
}
}
3.4 高级功能
拍摄视频
Future<void> _recordVideo() async {
final XFile? video = await _picker.pickVideo(source: ImageSource.camera);
if (video != null) {
// 处理视频
}
}
多选图片
Future<void> _selectMultipleImages() async {
final List<XFile>? images = await _picker.pickMultiImage();
if (images != null && images.isNotEmpty) {
// 处理多张图片
}
}
五、Flutter 调用原生代码
在 Flutter 中调用原生代码(如 Android 的 Java/Kotlin 或 iOS 的 Swift/Objective-C)需要使用 平台通道(Platform Channel)。以下是主要实现方式:
基本原理
MethodChannel 是 Flutter 与原生平台(Android、iOS)之间进行通信的一种机制,属于 Flutter 提供的三种通信通道之一(另外两种是 BasicMessageChannel 和 EventChannel)。它允许 Flutter 代码调用原生代码的方法,也允许原生代码调用 Flutter 代码的方法,实现双向通信。
实现步骤
在 Flutter 端创建通道
import 'package:flutter/services.dart';
class DeviceInfo {
static const MethodChannel _channel = MethodChannel('com.example.device_info');
// 获取设备信息
static Future<Map<String, dynamic>> getInfo() async {
try {
final Map<dynamic, dynamic> result = await _channel.invokeMethod('getDeviceInfo');
return Map<String, dynamic>.from(result);
} on PlatformException catch (e) {
throw Exception('Failed to get device info: ${e.message}');
}
}
Future<void> _fetchDeviceInfo() async {
try {
final info = await DeviceInfo.getInfo();
setState(() {
_deviceInfo = info;
});
} catch (e) {
print('Error: $e');
}
}
}
在 Android 端实现(Kotlin)
package com.example.device_info
import android.os.Build
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
class DeviceInfoPlugin : FlutterPlugin, MethodCallHandler {
private lateinit var channel: MethodChannel
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.example.device_info")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method == "getDeviceInfo") {
val deviceInfo = mutableMapOf<String, Any>()
// 添加设备信息
deviceInfo["model"] = Build.MODEL
deviceInfo["brand"] = Build.BRAND
deviceInfo["device"] = Build.DEVICE
deviceInfo["androidVersion"] = Build.VERSION.RELEASE
deviceInfo["sdkInt"] = Build.VERSION.SDK_INT
result.success(deviceInfo)
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
package com.example.my_app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import com.example.device_info.DeviceInfoPlugin
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// 注册插件
flutterEngine.plugins.add(DeviceInfoPlugin())
}
}
在 iOS 端实现(Swift)
import Flutter
import UIKit
public class DeviceInfoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(
name: "com.example.device_info",
binaryMessenger: registrar.messenger()
)
let instance = DeviceInfoPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getDeviceInfo" {
let device = UIDevice.current
let info = [
"model": device.model,
"systemName": device.systemName,
"systemVersion": device.systemVersion,
"localizedModel": device.localizedModel,
"name": device.name
]
result(info)
} else {
result(FlutterMethodNotImplemented)
}
}
}
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// 注册插件
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}