Topic List
cd /path/to/app-json-page
1. อัพเดตเวอร์ชันใน pubspec.yaml ให้ตรง (เช่น version: 0.1.0)
2. commit การเปลี่ยนแปลง
git add pubspec.yaml git commit -m "Release json_page v0.1.0"
3. สร้าง tag (ตั้งชื่อให้สื่อ เช่น json_page-v0.1.0)
git tag json_page-v0.1.0
4. push code + tag ขึ้น remote
git push origin main git push origin json_page-v0.1.0
เหตุการณ์ที่เกิดขึ้นคือ Mac เกิดอาการกระตุก trackpad กระโดด คุมไม่อยู่ เพลงที่เปิดเสียงติดๆ ขัดๆ ตรวจสอบ process CPU เป็นปกติ RAM กินเยอะมาก
เบื้องต้นมีปัญหาเรื่องการอ่าน/เขียน SSD คาดว่าเกิดจาก Spotlight จึงทำการปิดระบบ Spotlight ทั้งระบบไม่ให้ทำงาน
ลำดับเหตุการณ์ที่ทำให้เกิดปัญหานี้ (The Root Cause):
- Flutter Extension ตรวจเจอ Xcode: พอคุณเปิด VS Code ตัวปลั๊กอินเห็นว่าเครื่องนี้มีแอป Xcode ติดตั้งอยู่ (Xcode 26.6) มันจึงเข้าใจว่าคุณต้องการจะพัฒนาแอปสำหรับ iOS ด้วย [1]
- ปลั๊กอินพยายามสั่งเปิด simctl: ปลั๊กอินจึงส่งคำสั่ง xcrun simctl ไปยังระบบปฏิบัติการ เพื่อดึงรายชื่อเครื่องจำลอง iPhone/iPad ออกมาแสดงบนหน้าจอ [1]
- เกิดการ Crash (พัง) เบื้องหลัง: แต่เนื่องจากคุณไม่ได้กดดาวน์โหลดตัว iOS Simulator (Component) เก็บไว้ใน Xcode พอคำสั่ง simctl วิ่งไปหาไฟล์จำลองระบบแล้วไม่เจออะไรเลย มันจึงช็อกและปิดตัวเองลงทันที (Shutting down / Cleaning up ที่เราเห็นใน Log รอบแรก) [1]
- ติดลูปไร้สิ้นสุด: ตัว Flutter Extension ใน VS Code ไม่ยอมแพ้ พอมันเห็นว่ากระบวนการเช็กอุปกรณ์ดับไปโดยยังไม่ได้คำตอบ มันจึงสั่งรันคำสั่ง simctl ใหม่ซ้ำอีกครั้งในอีก 4 วินาทีถัดไป วนลูปไปเรื่อย ๆ จนทำให้ฮาร์ดดิสก์ทำงานหนัก [1]
สรุปคำแนะนำ
การที่เราแก้ปัญหาด้วยการเปลี่ยนชื่อไฟล์ simctl เป็น .bak ไปก่อนหน้านี้ ถือเป็นการแก้ทางที่ถูกต้องและปลอดภัยที่สุดสำหรับกรณีของคุณแล้วครับ เพราะเป็นการบอกตัวปลั๊กอินตรง ๆ ว่าไม่ต้องพยายามไปเรียกหาเครื่องจำลองที่ไม่มีอยู่จริงอีกต่อไป [1]
ตราบใดที่คุณยังไม่ได้ใช้ทดสอบ iOS คุณสามารถปล่อยให้ flutter doctor ขึ้นเครื่องหมาย ! แบบนี้ทิ้งไว้ได้เลย โดยไม่มีผลเสียใด ๆ กับการเขียนโค้ดฝั่ง Android หรือ Web ครับ [1]
แก้อาการอ่าน/เขียน SSD ตลอดเวลาเพื่อยืดอายุ SSD
จัดการกับ Spotlight
- คำสั่งปิดการทำงานทั้งหมดของ Spotlight:
bash
sudo mdutil -a -i off
- คำสั่งเปิดกลับมาใช้งานตามปกติ:
bash
sudo mdutil -a -i on
วิธีปิดการทำงาน mds_stores แบบถาวร
- ปิด
bash
sudo mdutil -a -i off
- ปิดกระบวนการที่ค้างอยู่ทันที:
bash sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.metadata.mds.plist<br />
แก้อาการ launchd ทำงานตลอดเวลาเพื่อ run app simctl
ตรวจสอบว่า launchd ว่ากำลังทำอะไรอยู่ด้วยคำสั่ง:
bash
log show --predicate 'process == "launchd"' --last 1m
พบว่าทุก 4 วินาทีจะทำการ run simctl แต่ไม่สำเร็จ ก็เลยสั่ง run ใหม่ทุก 4 วินาที ทำให้เกิดการทำงานที่ไม่ควรทำขึ้น
หลังจากพยายามมาหลายวิธีก็ยังไม่สำเร็จ จึงจัดการขั้นเด็ดขาดด้วยการ rename ชื่อ app simctl เพื่อไม่ให้ระบบ run app ได้อีกต่อไป
วิธีที่ 1: เปลี่ยนชื่อเครื่องมือค้นหา iOS เบื้องหลัง (เด็ดขาดและตรงจุดที่สุด)
เมื่อคุณไม่ได้ใช้ Xcode และไม่ได้ลงตัวจำลอง iOS ไว้ ไฟล์คำสั่ง simctl ในเครื่องคุณมักจะมาจากโฟลเดอร์ของ Command Line Tools เราจะไปทำการเปลี่ยนชื่อไฟล์หรือตัดสิทธิ์เพื่อไม่ให้ปลั๊กอิน Flutter สามารถหาคำสั่งนี้เจออีกต่อไป:
- เปิดแอป Terminal
- คัดลอกและวางคำสั่งด้านล่างนี้เพื่อตรวจสอบว่าระบบ macOS นำไฟล์ simctl ไปซ่อนไว้ที่ไหน:
bash
xcrun -f simctl
- โดยปกติระบบจะแสดงที่อยู่ไฟล์ เช่น /Library/Developer/CommandLineTools/usr/bin/simctl ให้คุณสั่งเปลี่ยนชื่อไฟล์นั้นเพื่อซ่อนมันจาก VS Code ด้วยคำสั่งนี้ครับ:
bash
sudo mv $(xcrun -f simctl) $(xcrun -f simctl).bak
(ระบบจะให้พิมพ์รหัสผ่านเครื่อง Mac ของคุณเพื่อยืนยัน)
- เมื่อเปลี่ยนชื่อไฟล์เสร็จแล้ว ต่อให้ VS Code หรือตัวปลั๊กอินจะพยายามสั่งรันถี่ยิบแค่ไหน ระบบก็จะขึ้นว่าหาไฟล์ไม่เจอ (Command Not Found) ทันที และลูปทุก 4 วินาทีจะหยุดทำงานอย่างถาวรครับ!
- ตรวจสอบว่า simctl ยังคง run อยู่อีกหรือไม่
bash
log show --predicate 'process == "launchd"' --last 1m
- อนาคตหากต้องทำงานกับ iOS ให้เปิดกลับมาใหม่ เนื่องจากได้ลองปิด services ไปหลายตัวมาก เช่น VSCODE, Flutter อาจะต้องเปิดกลับมาหลายรายการ
bash sudo mv $(xcrun -f simctl).bak $(xcrun -f simctl) flutter config --enable-ios
Create new app
flutter create --org com.yourdomain appname
Change app version
File: pubspec.yaml
version: 1.0.00+1
Create app icon
Add dependencie in file pubspec.yaml
And adddependencies: ... flutterlaunchericons: ^0.14.4
flutterlaunchericons: imagepath: "assets/icons/logo512.png" android: true ios: true removealphaios: true
Then run command in terminal
# dart run flutterlaunchericons:main
เอาคำว่า "DEBUG" ของเธอคืนไป
ใส่ไว้ใน MaterialApp
debugShowCheckedModeBanner: false
Change AndroidManifest.xml
File: android/app/src/main/AndroidManifest.xml
Change from
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
To
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:dist="http://schemas.android.com/apk/distribution"
>
Add permission
File: android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESSNETWORKSTATE" />
<uses-permission android:name="android.permission.WRITEEXTERNALSTORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESSFINELOCATION" />
<uses-permission android:name="android.permission.ACCESSCOARSELOCATION" />
<uses-feature android:name="android.hardware.camera" />
Change app label
File: android/app/src/main/AndroidManifest.xml
android:label="My App"
InAppWebView
Add InAppWebView provider
<provider
android:name="com.pichillilorenzo.flutterinappwebviewandroid.InAppWebViewFileProvider"
android:authorities="${applicationId}.flutterinappwebviewandroid.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILEPROVIDERPATHS"
android:resource="@xml/provider_paths" />
</provider>
Flutter command
Clean
flutter clean
Get
flutter pub get
Run release
flutter run --release
Add dependencies แล้ว แต่ import package ไม่ได้
Close project and re-open projectflutter packages get
Build app for Play Store
Generate JKS file : ถ้ายังไม่มีการสร้างไฟล์ .jks ให้สร้างขึ้นมาก่อน
ดูข้อมูลในไฟล์ .jkskeytool -genkey -v -keystore /Users/name/appname.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
ดูข้อมูลในไฟล์ .apkkeytool -v -list -keystore appname.jks
keytool -printcert -jarfile appname.apk
#กำหนดการ keystore ไว้ใน app สร้างไฟล์ [project]/android/key.properties แล้ว copy code ด้านล่างไปใส่
storePassword=<password from previous step> keyPassword=<password from previous step> keyAlias=upload storeFile=<location of the key store file, such as /Users/<user name>/upload-keystore.jks>
คำเตือน เก็บรักษาไฟล์ key.properties ไว้ให้ดี อย่างส่งขึ้น git เด็ดขาด
กำหนดค่าเพื่อ signing ใน gradle
1.แก้ไขไฟล์ [project]/android/app/build.gradle.kts
import java.util.Properties
import java.io.FileInputStream
val keyProperties = Properties().apply {
val file = rootProject.file("key.properties")
if (file.exists()) {
load(FileInputStream(file))
}
}
android {
...
}
2.หา buildTypes
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now,
// so flutter run --release works.
signingConfig signingConfigs.debug
}
}
แทนที่ด้วย
signingConfigs {
create("release") {
keyAlias = keyProperties["keyAlias"] as String
keyPassword = keyProperties["keyPassword"] as String
storeFile = keyProperties["storeFile"]?.let { file(it) }
storePassword = keyProperties["storePassword"] as String
}
// You can also create other signing configs like "debug" if needed
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
// signingConfig signingConfigs.release
}
}
Build
flutter build appbundle
ผลลัพท์ของการ build จะอยู่ที่
build/app/outputs/bundle/release/app-release.aab
ติดขั้นตอนอัพโหลดขึ้น play store
"Your Android App Bundle is signed with the wrong key. Ensure that your App Bundle is signed with the correct signing key and try again. Your App Bundle is expected to be signed with the certificate with fingerprint"
ลองหาทางแก้ไข ก็ไม่หาย แต่พอลองอัพหลายครั้ง ก็ยังขึ้น error แต่ปุ่มให้ SAVE สามารถใช้งานได้ ก็เลยผ่าน
ลองอัพ version ใหม่ ปรากฎว่า ไม่ขึ้น error
App Permission
หัวข้ออื่น
ที่มา
- มาลองสร้าง Flutter App กันเถอะ
- ประสบการณ์ Build flutter on iOS ครั้งแรก
- webview_flutter 2.0.2
- Different ways to change the status bar and navigation bar color (iOS and Android) in Flutter
- Flutter Launcher Icons
- How to use user-agent in webview-flutter Flutter?
- Flutter webview intercept and add headers to all requests
- Webview in flutter not working getting a platform error
- Flutter BottomNavigationBar not working with more than three items
ที่มา:
หลังจากติดตั้ง Flutter บน Macbook M1 pro ก็เกิดอาการ Exception: Error running pod install ตอนสั่ง flutter run บน iOS
ทางแก้ที่ลองแล้ว work คือ
1.Run คำสั่งในครั้งแรก (ครั้งเดียว)
sudo arch -x86_64 gem install ffi
2.ติดตั้ง pods
cd Project/ios arch -x86_64 pod install
จากคำสั่ง ดูเหมือนจะเป็นการติดตั้ง x86 แต่มันสามารถใช้งานได้ เลยไม่แน่ใจเหมือนกันว่าเป็นวิธีการแก้ปัญหาที่ถูกต้องไหม
ที่มา:
กำลังหาข้อมูลเกี่ยวกับการ build app with signed เพื่อส่งขึ้น Play Store
ทำตามขั้นตอนในเว็บ flutter.dev
ถ้ายังไม่มี java หรือ error ให้ลอง openjdk ก่อน
brew install openjdk export JAVA_HOME="/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home" java --version
สร้าง key : ถ้ายังไม่มีการสร้างไฟล์ .jks ให้สร้างขึ้นมาก่อน
ดูข้อมูลในไฟล์ .jkskeytool -genkey -v -keystore /Users/name/appname.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
ดูข้อมูลในไฟล์ .apkkeytool -v -list -keystore appname.jks
keytool -printcert -jarfile appname.apk
#กำหนดการ keystore ไว้ใน app สร้างไฟล์ [project]/android/key.properties แล้ว copy code ด้านล่างไปใส่
storePassword=<password from previous step> keyPassword=<password from previous step> keyAlias=upload storeFile=<location of the key store file, such as /Users/<user name>/upload-keystore.jks>
คำเตือน เก็บรักษาไฟล์ key.properties ไว้ให้ดี อย่างส่งขึ้น git เด็ดขาด
กำหนดค่าเพื่อ signing ใน gradle
1.แก้ไขไฟล์ [project]/android/app/build.gradle
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
...
}
2.หา buildTypes
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now,
// so flutter run --release works.
signingConfig signingConfigs.debug
}
}
แทนที่ด้วย
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
Build
flutter build appbundle
ผลลัพท์ของการ build จะอยู่ที่
build/app/outputs/bundle/release/app-release.aab
ติดขั้นตอนอัพโหลดขึ้น play store
"Your Android App Bundle is signed with the wrong key. Ensure that your App Bundle is signed with the correct signing key and try again. Your App Bundle is expected to be signed with the certificate with fingerprint"
ลองหาทางแก้ไข ก็ไม่หาย แต่พอลองอัพหลายครั้ง ก็ยังขึ้น error แต่ปุ่มให้ SAVE สามารถใช้งานได้ ก็เลยผ่าน
ลองอัพ version ใหม่ ปรากฎว่า ไม่ขึ้น error
ที่มา:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
class AboutScreen extends StatefulWidget {
static String routeName = "/about";
@override
_AboutScreenState createState() => _AboutScreenState();
}
class _AboutScreenState extends State<AboutScreen> {
PackageInfo _packageInfo = PackageInfo(
appName: 'Unknown',
packageName: 'Unknown',
version: 'Unknown',
buildNumber: 'Unknown',
);
//SharedPreferences.setMockInitialValues({});
@override
initState() {
super.initState();
_initPackageInfo();
}
Future<void> _initPackageInfo() async {
final PackageInfo info = await PackageInfo.fromPlatform();
log(info.toString());
setState(() {
_packageInfo = info;
});
}
Widget _infoTile(String title, String subtitle) {
return ListTile(
title: Text(title),
subtitle: Text(subtitle.isNotEmpty ? subtitle : 'Not set'),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// title: Text(widget.title),
title: Text('About us'),
),
body: Column(
// mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_infoTile('App name', _packageInfo.appName),
_infoTile('Package name', _packageInfo.packageName),
_infoTile('App version', _packageInfo.version),
_infoTile('Build number', _packageInfo.buildNumber),
],
),
);
}
}
ที่มา
iOS: in ios/Runner/Info.plist
ขอสิทธิ์ Location
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
ขอสิทธิ์ใช้กล้อง
<key>NSCameraUsageDescription</key> <string>Explanation on why the camera access is needed.</string>
ขอสิทธิ์ไมโครโฟน
<key>NSMicrophoneUsageDescription</key> <string>Flutter requires acess to microphone.</string>
Android: android/app/build.gradle
defaultConfig {
minSdkVersion 21
Android: android/app/src/main/AndroidManifest.xml
<application>
<provider
android:name="com.pichillilorenzo.flutterinappwebview.InAppWebViewFileProvider"
android:authorities="${applicationId}.flutterinappwebview.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILEPROVIDERPATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
Create file res/values/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths> </paths>
main.dart
import 'package:permission_handler/permission_handler.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Permission.camera.request();
runApp(MyApp());
}
สิทธิ์อื่นดูได้จาก Enable camera for HTML inputs
ที่มา
Step 1. Create a new application. The main screen in which will be StatefulWidget. Which should implement the WidgetsBindingObserver interface. Next, we get the instance of WidgetBinding and add an observer to it.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Tutorial Lifecycle'),
),
body: Center(),
);
}
}
Step 2. Now we have the didChangeAppLifecycleState method available. In this example, we simply print a state change to the thermal.
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
print('state = $state');
}
ที่มา Flutter App Lifecycle
เขียน Flutter App
Create new app
flutter create --org com.yourdomain appname
Android หากมี error gradle ให้แก้ไขไฟล์ android > gradle > gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
Set App signing team to your email
Open app using VSCode and run
Then build iodopen ios/Runner.xcworkspace
flutter build ios
In Runner / Targets/Runner -> select Team
If have already created a project: Change package name by add dependencies changeapppackagename: and run
apppackagename:main com.package.appnameflutter pub run change
Run in release mode
flutter run --release
Settings
- เปลี่ยนชื่อ App Icon
- Add permission: กำหนด Permission ด้วยนะ ไม่อย่างนั้นจะเปิด WebView เข้าเน็ตไม่ได้
iOS: แก้ไขไฟล์ ios/Runner/info.plist
<key>CFBundleName</key>
<string>APP NAME</string>
<key>io.flutter.embeddedviewspreview</key>
<string>YES</string>
Android manifest: แก้ไขไฟล์ android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="APP NAME"
>
macOS: in macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements
<key>com.apple.security.network.client</key>
<true/>
App Icon:
File : pubspec.yaml
dependencies: ... flutter_launcher_icons: ^0.9.0
flutter_icons:<br />
image_path: "assets/icon/logo_192.png"<br />
android: true
ios: true
remove_alpha_ios: true
# dart run flutter_launcher_icons:main
เอาคำว่า "DEBUG" ของเธอคืนไป
ใส่ไว้ใน MaterialApp
debugShowCheckedModeBanner: false
Add dependencies แล้ว แต่ import package ไม่ได้
close project and re-open projectflutter packages get
Upgrade version iOS แล้วมองไม่เห็น device
ให้ดาวน์โหลดไฟล์จาก GitHub แล้วนำไฟล์มาวางไว้ที่ (~/Application/) Xcode app ใน path Contents/Developer/Platform/iPhoneOS.platform/DeviceSupport
แล้วก็ Flutter Clean
flutter clean
App Permission
หัวข้ออื่น
ที่มา
- มาลองสร้าง Flutter App กันเถอะ
- ประสบการณ์ Build flutter on iOS ครั้งแรก
- webview_flutter 2.0.2
- Different ways to change the status bar and navigation bar color (iOS and Android) in Flutter
- Flutter Launcher Icons
- How to use user-agent in webview-flutter Flutter?
- Flutter webview intercept and add headers to all requests
- Webview in flutter not working getting a platform error
- Flutter BottomNavigationBar not working with more than three items