Flutter 程式寫作風格與管理 Code Organization

紅寶鐵軌客
Join to follow...
Follow/Unfollow Writer: 紅寶鐵軌客
By following, you’ll receive notifications when this author publishes new articles.
Don't wait! Sign up to follow this writer.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.
寫程式中、折磨中、享受中 ......
648   0  
·
2021/06/02
·
4 mins read


我們才剛開始寫 Flutter,只有兩個基本上空空的畫面 Screens,可是光是這樣,main.dart 裏面就已經有 120 行了,如果再繼續寫下去,很快就會有幾千行的程式碼,程式碼太長是很麻煩的,想想看,你要修改的程式內容一個在頭、一個在尾,改起來會有多麻煩,所以我們要將程式分段;如果能更進一步區分功能性整理就更好,將程式碼依照功能,放入適當的目錄 folder 中,也成為程式開發的必須,這就是 Code Organization。

有些開發平台會硬性規定程式碼怎麼放,Flutter 沒有硬性規定,所以百家爭鳴,有人分得很細,有人甚至不分,我是覺得分太細也麻煩,以下是參考 Dart 的官方風格建議+我喜歡的這個作法,基本上,就是將程式碼依照以下的目錄來分類:

  • blocs: BLoC,或是其他 state management。
  • components: 客製化組件。
  • models: 檔案資料。
  • screens: 畫面。
  • services: Web API,等。
  • theme: 主要是 style.dart,UI 設定。

及,統一的命名方式,建議的用法是:

  • 大駱駝 UpperCamelCase:Classes, enum types, typedefs, type, extensions;
  • 小駱駝 lowerCamelCase:Class members, top-level definitions, 常數, 變數, parameters;
  • 小寫底線 lowercase_with_underscores:檔名, libraries, packages/prefixes, directories, source;


我們現在只有兩個 screens,所以很簡單,就在放 main.dart 的目錄 lib 下新增一個 screens 的 Directory(在 lib 按右鍵就可以 new/directory),然後 create 兩個 dart 檔,我的檔名就是 screens 名稱,所以就是:

  • MyHomePage ⇒ my_home_page.dart
  • AudioSession ⇒ audio_session.dart

依照上面的寫作風格建議,檔名必須從大駱駝的 class 名稱改寫成小寫底線,這種寫法也能避免有些作業系統檔名不分大小寫的問題,接下來就是要設定 import(輸入),Andriod Studio 很聰明,如下圖,你只要打:import 就會出現指引,跟著 Android Studio 的指引走,選擇你要的檔案就好:

我是使用 import package,沒有用 part/part of 的主要原因是為了確保 private 變數的區隔性, 還記得我們曾經介紹過,底線 _ 開頭的函數或是變數就是 private 的,private 的變數就只能在這個 package 中有效,所以將程式碼用 package import 能保持程式碼的區隔。

每一個用到的 package 都要被 import 進來,所以:

  • main.dart:要 import material.dart, MyHomePage.dart 及 AudioSession.dart.
  • MyHomePage.dart:跟 main.dart 一樣;
  • AudioSession.dart:只要 import material.dart 就好。

好了,在我們繼續往前之前,我把到目前的程式碼都列出如下,讓大家可以比對同步一下:

lib/main.dart:

import 'package:flutter/material.dart';
import 'package:happy_recorder/screens/audio_session.dart';
import 'package:happy_recorder/screens/my_home_page.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Happy Recorder v0.0'),
    );
  }
}

lib/screens/my_home_page.dart 

import 'package:flutter/material.dart';
import 'package:happy_recorder/screens/my_home_page.dart';
import 'package:happy_recorder/screens/audio_session.dart';

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  } // _incrementCounter()

  void _openDrawer() {
    _scaffoldKey.currentState!.openDrawer();
  } // _openDrawer()

  void _closeDrawer() {
    Navigator.of(context).pop();
  } // _closeDrawer()

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
      ),
      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,
            ),
          ],
        ),
      ),
      drawer: Drawer(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text('This is the Drawer'),
              ElevatedButton(
                onPressed: _closeDrawer,
                child: const Text('Close Drawer'),
              ),
            ],
          ),
        ),
      ),
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(),
        child: Container(height: 30.0),
      ),
      floatingActionButton: FloatingActionButton(
        //onPressed: _incrementCounter,
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => AudioSession()),
          );
        },
        tooltip: 'new recording',
        child: Icon(Icons.add),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
    );
  }
}

lib/screens/audio_session.dart

import 'package:flutter/material.dart';

class AudioSession extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Record"),
        // automaticallyImplyLeading: false, // not display <- back btn
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

好啦,我們可以繼續往前走了,這樣介紹程式也更方便簡單了。





WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.


Article info

This article is part of:
Categories:
Tags:
Date:
Published: 2021/06/02 - Updated: 2022/03/01
Total: 932 words


Share this article:
About the Author

很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯




Join the discussion now!
Don't wait! Sign up to join the discussion.
WriterShelf is a privacy-oriented writing platform. Unleash the power of your voice. It's free!
Sign up. Join WriterShelf now! Already a member. Login to WriterShelf.