Project & Tooling
โครงสร้างโปรเจกต์
หัวข้อที่มีชื่อว่า “โครงสร้างโปรเจกต์”flutter create scaffold รูปทรงมาตรฐานที่ทุกโปรเจกต์ Flutter ใช้ร่วมกัน:
my_app/ lib/ # your Dart source — main.dart is the entry point test/ # unit/widget tests, mirrors lib/'s structure android/ ios/ web/ # per-platform native project (generated, rarely hand-edited) macos/ windows/ linux/ # desktop targets, same idea pubspec.yaml # the manifest — dependencies, assets, SDK constraint analysis_options.yaml # linter/analyzer configurationlib/ กับ test/ คือที่ที่คุณอยู่ทุกวัน; โฟลเดอร์ platform มีอยู่เพื่อให้แต่ละ target มี native project จริงอยู่ข้างใต้ (Gradle สำหรับ Android, Xcode project สำหรับ iOS/macOS และอื่น ๆ) — คุณจะเข้าไปแตะมันแค่ตอนตั้งค่าเฉพาะ platform (permission, native plugin setup, signing)
pubspec.yaml: manifest
หัวข้อที่มีชื่อว่า “pubspec.yaml: manifest”ทุกโปรเจกต์ Flutter ถูกอธิบายด้วย pubspec.yaml — dependency, dependency ที่ใช้แค่ตอน dev และ asset ที่ bundle เข้าแอป
name: my_appenvironment: sdk: ^3.13.0 # the Dart SDK that ships with Flutter 3.47
dependencies: flutter: sdk: flutter go_router: ^18.0.0 # runtime dependency
dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 # only used during development/tests
flutter: uses-material-design: true assets: - assets/images/ # a whole folder - assets/config.json # a single fileการแบ่งนี้สำคัญ: dependencies ถูก ship เข้าไปในแอปของคุณ; dev_dependencies (test framework, linter, code generator) ไม่ถูก asset ที่ list ไว้ที่นี่คืออันเดียวที่ถูก bundle — การลืม entry คือสาเหตุปกติของ “asset not found”
flutter CLI
หัวข้อที่มีชื่อว่า “flutter CLI”คำสั่ง flutter คือทางเข้าสู่ทุกอย่าง:
flutter create my_app # scaffold a new projectflutter pub get # resolve and download dependenciesflutter run # run on a connected device/emulatorflutter run --release # run in release modeflutter build apk # produce a release buildflutter doctor # diagnose your local toolchainflutter analyze # static analysis / lintsflutter doctor คือคำสั่งแรกที่ควรรันเมื่อมีอะไรผิดปกติ — output จริงจาก Mac เครื่องนี้เป๊ะ (Flutter 3.47.5):
Doctor summary (to see all details, run flutter doctor -v):[✓] Flutter (Channel stable, 3.47.5, on macOS 26.6.2 25G83 darwin-arm64, locale en-TH)[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)[✓] Xcode - develop for iOS and macOS (Xcode 26.2)[✓] Chrome - develop for the web[✓] Connected device (2 available)[✓] Network resources
• No issues found!มันเช็ค SDK, device toolchain ของทุก platform และ IDE plugin แล้วบอกตรง ๆ ว่าอะไรขาดหาย — บรรทัด [✗] สีแดงจะระบุสิ่งที่ต้องแก้เจาะจง (license ของ Android ที่ยังไม่ accept, component ของ Xcode ที่ขาด, …) แทนที่จะเป็นความล้มเหลวทั่ว ๆ ไป
FVM: การ pin เวอร์ชัน Flutter SDK ต่อโปรเจกต์
หัวข้อที่มีชื่อว่า “FVM: การ pin เวอร์ชัน Flutter SDK ต่อโปรเจกต์”FVM (Flutter Version Management, fvm.app) คือเครื่องมือมาตรฐานของ community สำหรับปัญหาเดียวกับที่คอร์สนี้เองต้องจัดการ: การ pin เวอร์ชัน Flutter SDK ที่เจาะจงต่อโปรเจกต์ (.fvmrc / .fvm/fvm_config.json) แทนที่จะใช้เวอร์ชันที่บังเอิญอยู่บน PATH มันไม่ได้เป็นส่วนหนึ่งของ Flutter SDK เอง — มันเป็น CLI แยกที่จัดการ Flutter SDK ที่ cache ไว้หลายเวอร์ชันและ symlink ตัวที่ถูกต้องเข้าแต่ละโปรเจกต์ — แต่มันคือคำตอบมาตรฐานของ “ทำไม build บนเครื่องฉันถึงต่างจาก CI” เมื่อสาเหตุจริงคือ Flutter สองเวอร์ชันที่ต่างกัน
analysis_options.yaml: ความเข้มงวดที่จับบั๊กจริงได้
หัวข้อที่มีชื่อว่า “analysis_options.yaml: ความเข้มงวดที่จับบั๊กจริงได้”analysis_options.yaml config analyzer เกินกว่าชุด lint พื้นฐาน สาม flag — strict-casts, strict-inference และ strict-raw-types — ไม่ใช่ส่วนหนึ่งของ lint package ไหนเลย มันคือ analyzer language option ที่ปิดโดย default ที่จับบั๊กประเภทเจาะจง: โค้ดที่ “ทำงาน” ได้แค่เพราะ dynamic แบบ implicit
include: package:flutter_lints/flutter.yaml
analyzer: language: strict-casts: true strict-inference: true strict-raw-types: trueprobe ของคอร์สนี้เองรันด้วย config นี้เป๊ะ — diagnostic ทุกอันด้านล่างเป็นของจริง จับได้จากการรัน flutter analyze กับไฟล์ demo เล็ก ๆ ที่เปิดสาม flag นี้
// @expect-error strict-casts: real diagnostic captured with strict-casts:// true enabled, from a scratch file inside this course's own probedynamic getValue() => 42;
int useStrictCasts() { int value = getValue(); // implicit dynamic -> int cast return value;}error • A value of type 'dynamic' can't be assigned to a variable of type 'int'. Try changing the type of the variable, or casting the right-hand type to 'int' • invalid_assignmentถ้าไม่มี strict-casts การแคบลงแบบ implicit จาก dynamic นี้ถูกอนุญาตแบบเงียบ ๆ — เป็นแหล่งจริงของบั๊กแบบ “มันทำงานในโค้ดพาธนี้แต่ crash ด้วย type error ที่อื่นตอน runtime” ซึ่งตอนนี้ถูกจับตอน analyze แทน
// @expect-error strict-raw-types: real diagnostic captured with// strict-raw-types: true enabledList useStrictRawTypes() { final List list = []; // raw type, no type argument return list;}warning • The generic type 'List<dynamic>' should have explicit type arguments but doesn't. Use explicit type arguments for 'List' • strict_raw_type// @expect-error strict-inference: real diagnostic captured with// strict-inference: true enabledList computeList() { final result = []; // inferred as List<dynamic> -- strict-inference flags this return result;}warning • The type argument(s) of 'List' can't be inferred. Use explicit type argument(s) for 'List' • inference_failure_on_collection_literalวิธีแก้ทั้งสามอันเหมือนกัน: ระบุให้ชัดเจน
int getIntValue() => 42; // no dynamic return type to begin with
int useStrictCastsFixed() { final int value = getIntValue(); // no implicit cast needed return value;}
List<String> useStrictRawTypesFixed() { final List<String> list = <String>[]; // explicit type argument return list;}
List<int> computeListFixed() { final result = <int>[]; // explicit type argument return result;}fence นี้ analyze สะอาดเทียบกับ setting เข้มงวดเดียวกันเป๊ะ — การรัน flutter analyze กับมันรายงานศูนย์ issue
flutter_lints เทียบกับ very_good_analysis พิสูจน์ด้วยช่องว่างจริง
หัวข้อที่มีชื่อว่า “flutter_lints เทียบกับ very_good_analysis พิสูจน์ด้วยช่องว่างจริง”ทั้งคู่เป็น lint-rule package ที่คุณ include: จาก analysis_options.yaml; มันไม่เท่ากัน และความต่างก็ไม่ได้อยู่ตรงจุดที่คุณคาดเสมอไป เช็ค rule set จริงที่ติดตั้งอยู่บนเครื่องนี้ของทั้งสอง package (flutter_lints resolve เป็น 6.0.0, very_good_analysis resolve เป็น 11.0.0 ผ่าน flutter pub add --dry-run เทียบกับ pub.dev ปัจจุบัน):
flutter_lints6.0.0 (include: package:flutter_lints/flutter.yaml) เพิ่มชุดเล็ก ๆ เฉพาะ Flutter ทับpackage:lints/recommended.yaml:avoid_print,avoid_unnecessary_containers,avoid_web_libraries_in_flutter,no_logic_in_create_state,prefer_const_constructors_in_immutables,sized_box_for_whitespace,sort_child_properties_last,use_build_context_synchronously,use_full_hex_values_for_flutter_colors,use_key_in_widget_constructors— ค่า default ที่เล็กและ friction ต่ำโดยตั้งใจvery_good_analysis11.0.0 ใหญ่กว่ามาก (237 บรรทัดของ rule) และมีprefer_const_constructorsแบบทั่วไปด้วย — การอ่านlib/flutter.yamlของflutter_lintsทีละบรรทัดแสดงว่าตรงกันข้าม: มันมีแค่prefer_const_constructors_in_immutablesที่แคบกว่า (ทำงานเฉพาะการเรียก constructor ข้างใน class ที่ mark ด้วย@immutable) ไม่ใช่ rule ทั่วไปที่บทพิสูจน์ const-skip ตลอดทั้งบทเรียน rebuilds-and-performanceใช้อยู่
ข้อสรุปเชิงปฏิบัติ: flutter_lints เพียงอย่างเดียวจะไม่ flag การขาด const บน call site ของ StatelessWidget ธรรมดา — ถ้าชัยชนะจุดนี้สำคัญกับทีมของคุณในฐานะ rule ที่บังคับใช้ (ไม่ใช่แค่นิสัย) ให้เพิ่ม prefer_const_constructors ตรง ๆ ใต้ linter: rules: ของโปรเจกต์เอง หรือใช้ very_good_analysis ซึ่งมีมันมาให้ตั้งแต่แรกพร้อม baseline ที่เข้มงวดกว่ามาก
dart fix --dry-run: output จริง
หัวข้อที่มีชื่อว่า “dart fix --dry-run: output จริง”dart fix ใช้ auto-fix ที่ analyzer ลงทะเบียนไว้เอง --dry-run แสดงสิ่งที่จะเปลี่ยนโดยไม่แตะอะไรเลย:
// @expect-error use_full_hex_values_for_flutter_colors: real diagnostic +// dart fix --dry-run output captured from a scratch file inside this// course's own probe (see the info-level output quoted below)import 'package:flutter/material.dart';
const Color brandColor = Color(0xFFFFF); // triggers use_full_hex_values_for_flutter_colorsflutter analyze บนไฟล์นี้รายงาน diagnostic จริง:
info • Instances of 'Color' should be created using an 8-digit hexadecimal integer (such as '0xFFFFFFFF') • use_full_hex_values_for_flutter_colorsการรัน dart fix --dry-run กับไฟล์ที่มีบรรทัดนี้เป๊ะ (จับมาจาก probe ของคอร์สนี้เอง):
Computing fixes in scratch_fix (dry run)...
1 proposed fix in 1 file.
fixable_demo.dart use_full_hex_values_for_flutter_colors - 1 fix
To fix an individual diagnostic, run one of: dart fix --apply --code=use_full_hex_values_for_flutter_colors lib/scratch_fix
To fix all diagnostics, run: dart fix --apply lib/scratch_fixแต่ไม่ใช่ทุก lint ที่ auto-fix ได้ — พิสูจน์แล้วขณะสร้างตัวอย่างนี้เป๊ะ ๆ: avoid_unnecessary_containers (diagnostic จริงที่มีชีวิตจาก flutter analyze) รายงานศูนย์fix ที่เสนอจาก dart fix --dry-run บนไฟล์เดียวกัน เพราะการลบ Container พร้อมย้าย child ของมันให้ถูกต้องคือการแก้เชิงโครงสร้างที่ fix registry ไม่พยายามทำอัตโนมัติ dart fix มีประโยชน์จริง แต่ “analyzer flag มัน” กับ “dart fix แก้มันได้” เป็นสองข้อเท็จจริงที่พิสูจน์ได้อิสระจากกัน
dart:io เทียบกับ web — พิสูจน์ด้วยการ build จริง (แก้ข้อสมมติฐานที่พบบ่อย)
หัวข้อที่มีชื่อว่า “dart:io เทียบกับ web — พิสูจน์ด้วยการ build จริง (แก้ข้อสมมติฐานที่พบบ่อย)”ข้อสมมติฐานที่พบบ่อยคือ flutter build web จะพังทันทีที่คุณ import 'dart:io' แล้วแตะ File การรันมันจริง ๆ ใน scratch Flutter app ที่มีการเรียก dart:io File ตรง ๆ ต่อเข้ากับ main() (probe ของคอร์สนี้ไม่เคยได้รับ import ที่เข้ากับ web ไม่ได้เลย ตาม shared brief — สิ่งนี้รันใน scratch ไม่ใช่ probe):
// scratch/lib/dart_io_direct.dart -- NOT run in the shared probe, per the briefimport 'dart:io';
String readConfigPath() { final file = File('config.json'); return file.path;}
bool isIOSPlatform() => Platform.isIOS;
Future<String> readConfigContents() async { final file = File('config.json'); return file.readAsString();}$ flutter build webCompiling lib/main.dart for the Web...Font asset "MaterialIcons-Regular.otf" was tree-shaken, reducing it from 1645184 to 7800 bytes (99.5% reduction).Compiling lib/main.dart for the Web... 13.6s✓ Built build/webมันสำเร็จ— พร้อมทั้ง Platform.isIOS และ File.readAsString() ที่ถูก compile เข้าไปและถูกเรียกจาก main() ผลลัพธ์เหมือนกันกับ flutter build web --wasm เหตุผล พิสูจน์ด้วยการอ่าน source ของ web patch ของ SDK ที่ติดตั้งจริงตัวนี้เอง (~/flutter/bin/cache/dart-sdk/lib/_internal/js_runtime/lib/io_patch.dart): class ของ dart:io มีอยู่เป็น stub ที่ type-check ได้จริงบน web compiler — native operation ทุกตัวแค่ throw ตอน runtime แทนที่จะ compile ไม่ผ่าน:
@patchclass _File { @patch static _exists(_Namespace namespace, Uint8List path) { throw UnsupportedError("File._exists"); } @patch static _create(_Namespace namespace, Uint8List path, bool exclusive) { throw UnsupportedError("File._create"); } // ...every other native File/Directory/FileStat operation follows the same pattern}นี่คืออันตรายจริง และมันแย่กว่าการ build พังเสียอีก: บั๊กนี้ ship ผ่านทุก gate แบบเงียบ ๆ (flutter analyze, flutter build web, flutter build web --wasm — ไม่มีอันไหนจับมันได้เลย) และจะโผล่มาเป็น UnsupportedError จริงแค่ครั้งแรกที่ browser ของผู้ใช้รันโค้ดพาธนั้นจริง ๆ ใน production environment ของคอร์สนี้ไม่มี browser ให้รันและจับ runtime crash นั้นตรง ๆ — อ้างอิงจาก source ด้านบน อย่างตรงไปตรงมา แทนที่จะยืนยันโดยไม่มีหลักฐาน
kIsWeb (จาก package:flutter/foundation.dart) คือ runtime check ที่โค้ดส่วนใหญ่หยิบมาใช้ — if (!kIsWeb) { file.readAsStringSync(); } — และมันทำงานได้จริงในฐานะ runtime guard แต่มันไม่เปลี่ยนสิ่งที่ถูก compile: โค้ดที่ import dart:io ยังคงเป็นส่วนหนึ่งของ web bundle ไม่ว่าจะแบบไหน (พิสูจน์แล้วด้านบน — การ build ไม่เคยพังแม้แต่เวอร์ชันที่ไม่มี guard) ดังนั้นเช็ค kIsWeb เพียงอย่างเดียวไม่ให้สัญญาณตอน compile-time ถ้าคุณลืมมันที่ไหนสักแห่ง วิธีแก้ที่แข็งแรงกว่าคือ conditional import/export ซึ่งสลับทั้ง implementationต่อ platform แทนที่จะแบ่งแยกด้วย runtime branch ข้างในตัวเดียว:
String readConfigValue() => throw UnsupportedError('No platform implementation selected');import 'dart:io';
String readConfigValue() => Platform.environment['CONFIG'] ?? 'io-default';String readConfigValue() => 'web-default'; // no dart:io reference at allexport 'platform_stub.dart' if (dart.library.io) 'platform_io_impl.dart' if (dart.library.js_interop) 'platform_web_impl.dart';import 'package:flutter_test/flutter_test.dart';import 'package:probe/lessons/tooling-and-production__project-and-tooling/platform_config.dart';
void main() { test( 'the conditional export selects the dart:io implementation under flutter test (native VM)', () { expect(readConfigValue(), 'io-default'); });}00:00 +0: the conditional export selects the dart:io implementation under flutter test (native VM)
00:00 +1: All tests passed!dart.library.io เป็น true ภายใต้ flutter test (process VM แบบ native) ดังนั้น test นี้พิสูจน์ว่า branch io_impl คือตัวที่ถูกเลือกจริง ๆ ไม่ใช่แค่ไฟล์ compile ผ่าน บน web บรรทัด export เดียวกัน resolve dart.library.js_interop แทน และสลับเป็น platform_web_impl.dart ซึ่งไฟล์ของมันไม่มี import dart:io ให้ compile เข้าไปเลยด้วยซ้ำ universal_io (package จาก pub.dev ที่ให้ API รูปทรงเหมือน dart:io ที่ปลอดภัยกับ web จริง ๆ สำหรับส่วน HTTP-client) คือเวอร์ชันสำเร็จรูปของ pattern นี้สำหรับกรณีทั่วไปที่ต้องการโค้ดรูปทรง HttpClient ที่รันบน web ได้ด้วย
DevTools
หัวข้อที่มีชื่อว่า “DevTools”DevTools คือชุดเครื่องมือบน browser สำหรับตรวจสอบแอปที่กำลังรัน: widget inspector (แตะ widget ดู tree กับ constraint ของมัน), มุมมอง performance/timeline (หาจังหวะสะดุด ครอบคลุมพร้อม workflow จริงของมันเองในบทเรียน rebuilds-and-performance), มุมมอง memory และมุมมอง network มันคือวิธีที่คุณเห็นต้นไม้และ pipeline ที่โมดูลก่อน ๆ อธิบายไว้ — environment ของคอร์สนี้ไม่มีอุปกรณ์ให้ต่อมัน ดังนั้นทุก claim เกี่ยวกับมันในคอร์สนี้อ้างอิงจาก docs ไม่เคยเป็น screenshot
Hot reload เทียบกับ hot restart
หัวข้อที่มีชื่อว่า “Hot reload เทียบกับ hot restart”คำสั่ง refresh สองตัวนี้ไม่เหมือนกัน และการรู้ความต่างช่วยลดความสับสนจริง:
- Hot reload (
r) ฉีด source ที่เปลี่ยนเข้าไปในแอปที่กำลังรันอยู่แล้ว rebuild widget tree — State ถูกรักษาไว้ มันเร็วกว่าหนึ่งวินาทีและเป็นเหตุผลที่การพัฒนา Flutter รู้สึกเร็ว - Hot restart (
R) ทิ้ง state ของแอปแล้ว restart จากmain()— ช้ากว่า แต่ได้กระดานเปล่าสะอาด
Hot reload มีข้อจำกัด มันรัน build ซ้ำ แต่ไม่รัน initState หรือ main() ที่เปลี่ยนซ้ำ และแก้บางอย่างไม่ได้ (การประเมิน const ใหม่ การเปลี่ยนรูปทรงของ enum/class, ตัวเริ่มต้นของ global variable) เมื่อการเปลี่ยนแปลงดูเหมือนไม่มีผล คำตอบมักจะเป็น hot restart
Reading AI-generated code
หัวข้อที่มีชื่อว่า “Reading AI-generated code”โค้ด project setup/config ที่ AI generate มักสมมติ baseline ของ lint/analyzer ที่เข้มงวดกว่าที่ config ไว้จริง หรือหยิบ dart:io มาใช้ในโค้ดที่ตั้งใจให้แชร์ข้าม platform โดยไม่เช็คก่อน
// AI-generated: a shared "read local settings file" helper,// used from both a mobile screen and (the AI assumed) a web build tooimport 'dart:io';
class SettingsRepository { Future<String> loadRawSettings() { return File('settings.json').readAsString(); }}ก่อนจะเชื่อโค้ด project/tooling ที่ generate มาแบบนี้ ให้เช็ค:
- import
dart:ioในไฟล์ใดก็ตามที่ web build target เข้าถึงได้ — พิสูจน์แล้วด้านบนว่ามัน compile ผ่านและ ship แบบเงียบ ๆ มีแค่ conditional import/export (หรือuniversal_io) เท่านั้นที่กันมันออกจาก web bundle จริง ๆ - สมมติว่า
prefer_const_constructors(หรือ lint เจาะจงไหนก็ตาม) active อยู่แค่เพราะมีflutter_lints— พิสูจน์แล้วด้านบนว่าflutter_lints6.0.0 ไม่เปิดมันโดย default; เช็ค rule file ของ package ที่ติดตั้งจริง อย่าสมมติจากชื่อ - โค้ดที่ “compile ผ่าน” ได้แค่เพราะ
dynamicแบบ implicit ที่ไหนสักแห่ง — พิสูจน์แล้วด้านบนว่าstrict-casts/strict-inference/strict-raw-typesจับบั๊กประเภทนี้เป๊ะ แต่แค่ถ้าanalysis_options.yamlของโปรเจกต์เปิดมันจริง ๆ (มันปิดโดย default แม้จะมีflutter_lintsรวมอยู่ก็ตาม)
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”ทุกชั้นในบทนี้คือแบบต่างกันของ “อะไรที่ฉันตกลงไว้จริง เทียบกับอะไรที่ฉันสมมติเอาเอง” pubspec.yaml คือสัญญาที่ชัดเจนสำหรับสิ่งที่ ship ชื่อของ lint package (flutter_lints, very_good_analysis) ไม่ใช่สัญญาเอง — ไฟล์ .yaml rule จริงที่มัน resolve ไปคือสัญญา และการอ่านมันตรง ๆ (แบบที่บทนี้ทำ จนเจอช่องว่างของ prefer_const_constructors) คือวิธีเดียวที่จะรู้แน่ชัดว่าอะไรถูกบังคับใช้ strict-casts/strict-inference/strict-raw-types มีอยู่เพราะ type system ของ Dart มีทางหนี (dynamic) ที่เงียบโดย default — สาม flag นี้คือวิธีที่คุณบอก analyzer ว่า “หยุดให้ฉันใช้มันโดยไม่ตั้งใจ” และ dart:io บน web คือเวอร์ชันที่คมที่สุดของบทเรียนนี้: ความเงียบของ compiler (build ที่สำเร็จ) ไม่ใช่สิ่งเดียวกับความปลอดภัย พิสูจน์แล้วด้านบนด้วย build ที่สำเร็จจริง ๆ ในขณะที่ ship runtime crash ที่รับประกันได้
Trade-offs
หัวข้อที่มีชื่อว่า “Trade-offs”| ตัวเลือก | ข้อดี | ต้นทุน |
|---|---|---|
flutter_lints (default จาก flutter create) | เล็ก friction ต่ำ เฉพาะ Flutter — พิสูจน์แล้วด้านบน สิบ rule ทับ lints/recommended.yaml | พิสูจน์แล้วด้านบน: ไม่บังคับ prefer_const_constructors หรือ rule ด้าน performance/safety อีกหลายตัวที่ very_good_analysis มี |
very_good_analysis | baseline เข้มงวดกว่ามาก (237 บรรทัดของ rule) — พิสูจน์แล้วด้านบนว่ามี prefer_const_constructors | friction ล่วงหน้ามากกว่าบน codebase ที่มีอยู่แล้ว; น่าจะเผยการละเมิดที่มีอยู่เดิมจำนวนมากพร้อมกัน |
strict-casts/strict-inference/strict-raw-types เปิด | จับบั๊ก implicit-dynamic จริงตอน analyze พิสูจน์แล้วด้านบนด้วย diagnostic จริงสามตัว | ต้องมี type annotation ที่ชัดเจนมากขึ้นทั่ว codebase |
| Conditional import/export สำหรับโค้ดเฉพาะ platform | พิสูจน์แล้วด้านบน: โค้ดของ implementation ที่ไม่ถูกเลือก (และ import ของมัน) ไม่เป็นส่วนหนึ่งของ bundle platform นั้นจริง ๆ | ไฟล์/boilerplate มากกว่า branch if (kIsWeb) เดียว |
runtime guard kIsWeb เพียงอย่างเดียว | ง่าย ไฟล์เดียว ไม่ต้องมีโครงสร้างเพิ่ม | พิสูจน์แล้วด้านบน: ไม่เอาโค้ดที่ guard ไว้ (หรือ import ของมัน) ออกจาก web bundle — แค่ป้องกันไม่ให้มันรันเท่านั้น |
Common Misconceptions
หัวข้อที่มีชื่อว่า “Common Misconceptions”- “การมี
flutter_lintsหมายความว่าprefer_const_constructorsถูกบังคับใช้” พิสูจน์แล้วด้านบนว่าไม่จริง จากการอ่าน rule file จริงของflutter_lints6.0.0: มันมีแค่prefer_const_constructors_in_immutablesที่แคบกว่า - “
strict-casts/strict-inference/strict-raw-typesคือ lint ดังนั้น lint package อย่างflutter_lintsเปิดมันให้” พิสูจน์แล้วด้านบนว่าไม่จริง: มันคือ analyzerlanguageoption config ไว้ใต้ YAML key ที่ต่างไปโดยสิ้นเชิง (analyzer: language:) ปิดโดย default ไม่ว่าจะรวม lint package ไหนก็ตาม - “ถ้า
dart fix --dry-runเจอ diagnostic มันแก้ได้เสมอ” พิสูจน์แล้วด้านบนว่าไม่จริง:avoid_unnecessary_containersเป็น diagnostic จริงที่มีชีวิตแต่มีศูนย์ auto-fix ที่เสนอ บนไฟล์เดียวกันที่use_full_hex_values_for_flutter_colorsมีอันนึง - “
flutter build webจะ compile ไม่ผ่านถ้าโค้ด importdart:io” พิสูจน์แล้วด้านบนว่าไม่จริงด้วยการ build จริง: มันสำเร็จ แม้เรียกPlatform.isIOSและFile.readAsString()จากmain()— ความล้มเหลว พิสูจน์แล้วจาก source เกิดแค่ตอน runtime ใน browser ครั้งแรกที่โค้ดพาธนั้นรันจริง - “เช็ค
kIsWebก็พอทำให้โค้ดที่แตะdart:ioปลอดภัยกับ web” พิสูจน์แล้วด้านบนว่าไม่จริง: โค้ด (และ importdart:ioของมัน) ยังคง compile เข้าไปใน web bundle ไม่ว่าจะแบบไหน มีแค่ conditional import/export เท่านั้นที่เปลี่ยน implementation — และ import — ที่มีอยู่จริงต่อ platform