ram2 ๐Ÿš—

[flutter] ์ƒˆ๋กญ๊ฒŒ ๋ฐ”๋€ codelab (step5) ๋ณธ๋ฌธ

๐Ÿ’ง flutter

[flutter] ์ƒˆ๋กญ๊ฒŒ ๋ฐ”๋€ codelab (step5)

coram22 2023. 2. 8. 20:30
728x90
๋ฐ˜์‘ํ˜•
728x90

์ง€๋‚œ ์Šคํ…๊นŒ์ง€ ์ž˜ ๋”ฐ๋ผ์™”๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์„ ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ ๋ชจ๋‘ ๋Š๋ผ๋“ฏ, ํ˜„์žฌ๋Š” ํŒจ๋”ฉ๋„ ๋งž์ง€ ์•Š๊ณ , ์šฐ๋ฆฌ๊ฐ€ ์ค‘์š”ํ•˜๊ฒŒ ๋ณด์—ฌ์ค˜์•ผ ํ•  ๋ฐ์ดํ„ฐ์ธ word๊ฐ€ ํ•œ ๋ˆˆ์— ๋“ค์–ด์˜ค์ง€ ์•Š๋Š”๋‹ค.

์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋Š๋ผ๊ธฐ์— ์กฐ๊ธˆ ๋” ์‰ฝ๊ฒŒ, ์›ํ•˜๋Š” ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•œ๋‹ค. 

 

๊ทธ๋ž˜์„œ ์ด๋ฒˆ ์Šคํ…์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์‹œ๊ฐ์ ์ธ ์š”์†Œ(๋””์ž์ธ) ์— ์กฐ๊ธˆ ์‹ ๊ฒฝ์„ ์จ๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ฝ”๋“œ๋žฉ์—์„œ ์ œ์‹œํ•œ ๋‹ค์Œ ์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ๋Š” ์ด ์ด๋ฏธ์ง€์™€ ๊ฐ™๋‹ค.

์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ !!

 

Extract a widget

ํ˜„์žฌ ์šฐ๋ฆฌ ์ฝ”๋“œ์—์„œ๋Š” word pair๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ด๊ณ  ์žˆ๋‹ค.

Text(appState.current.asLowerCase)

์ด ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ๋” ๋ณต์žกํ•˜๊ฒŒ ๋ฐ”๊ฟ”๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ ๋ณ„๋„์˜ ์œ„์ ฏ์œผ๋กœ ๋นผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ํ•œ๋‹ค. 

์ด์ฒ˜๋Ÿผ UI์˜ ๊ฐ ์š”์†Œ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์œ„์ ฏ์„ ๊ฐ–๋Š” ๊ฒƒ์€ Flutter์—์„œ ์ฝ”๋“œ์˜ ๋ณต์žก์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ์ค‘์š”ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค. 

(+ ๋‚ด ๊ฒฝํ—˜์— ๋น„์ถ”์–ด๋ณด๋ฉด, ์ด๋ฅผ ๋”ฐ๋กœ ์œ„์ ฏ์œผ๋กœ ๋นผ์„œ ๋งŒ๋“ค์–ด๋‘๋ฉด ๋‚˜์ค‘์— ๋˜ ์“ฐ์ผ ๋•Œ ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ์งœ๊ฑฐ๋‚˜, ์ด์ „ ์ฝ”๋“œ๋ฅผ ์ฐพ์•„ ๋ณต๋ถ™ํ•˜๋Š” ์ผ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋˜ํ•œ, ์ˆ˜์ •์‚ฌํ•ญ์ด ์žˆ์„ ๋•Œ๋„ ์‰ฝ๊ฒŒ ์ฐพ์•„์„œ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ž‘์€ ๋‹จ์œ„๋กœ ์œ„์ ฏ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค !!)

 

๋ฌผ๋ก  Flutter์—์„œ๋Š” ์œ„์ ฏ์„ extract ํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๊ฐ€ ์žˆ์ง€๋งŒ, ์œ„์ ฏ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— extract๋˜๋Š”์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์—๋งŒ ์ ‘๊ทผํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์šฐ๋ฆฌ ์ฝ”๋“œ๋ฅผ ์˜ˆ๋กœ ๋ณด๋ฉด, ํ˜„์žฌ appState์— ์ ‘์†ํ•˜์ง€๋งŒ, ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์ •๋ณด๋Š” ํ˜„์žฌ ๋‹จ์–ด ์Œ๋งŒ ์•Œ๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ์ด์ œ  MyHomePage  ์œ„์ ฏ์˜ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ”๋ณด์ž..!!

 

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();  
    var pair = appState.current;                 // ← Add this.

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(pair.asLowerCase),                // ← Change to this.
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],      // ← 7
      ),
    );
  }
}

// ...

์ด์ œ Text ์œ„์ ฏ์€ ๋”์ด์ƒ appState๋ฅผ ์–ธ๊ธ‰ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

๊ทธ๋ฆฌ๊ตฌ, ์ด์ œ Refactor ๋ฉ”๋‰ด๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์œ„์ ฏ extract๋ฅผ ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

1. refactor ํ•˜๊ณ  ์‹ถ์€ ์œ„์ ฏ์—์„œ ์šฐํด๋ฆญ -> Refactor ๋ฉ”๋‰ด๋ฅผ ์„ ํƒ -> "BigCard" ์ž…๋ ฅ -> ์—”ํ„ฐ

 

2. Move your cursor to the piece code you want to refactor (Text, in this case), and press Ctrl+. (Win/Linux) or Cmd+. (Mac). (๋‚˜๋งŒ ํ—ท๊ฐˆ๋ ธ๋‚˜.. Cmd๋ž‘ .์„ ๊ฐ™์ด ๋ˆ„๋ฅด๋ฉด ๋œ๋‹ค..!!!)

์ดํ›„ ๋ฐฉ๋ฒ•์€ ์œ„ ์˜์ƒ์„ ๋”ฐ๋ผํ•˜๋ฉด ๋œ๋‹ค ! 

 

์ด์ฒ˜๋Ÿผ extract widget ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž˜ ๋”ฐ๋ผํ–ˆ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค๊ฐ€ ํ•˜๋‚˜ ์ƒ์„ฑ๋์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค !

// ...
class BigCard extends StatelessWidget {
  const BigCard({
    Key? key,
    required this.pair,
  }) : super(key: key);

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    return Text(pair.asLowerCase);
  }
}
// ...

์ด์ œ ์ด refactoring์„ ํ†ตํ•ด ์•ฑ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

 

Add a Card

์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋ชฉํ‘œ๋ผ๊ณ  ํ–ˆ๋˜ ๊ทธ ํ™”๋ฉด์„ ๊ธฐ์–ต ํ•˜๋Š”๊ฐ€...?

์šฐ๋ฆฌ์˜ ๋ชฉํ‘œ !!

์ด ์ด๋ฏธ์ง€๋ฅผ ๋ณด๋ฉด vastweb์ด ํŒŒ๋ž€์ƒ‰ ์นด๋“œ ์•ˆ์— ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด์ œ ์นด๋“œ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ๊ฒƒ์ด๋‹ค !

์•„๊นŒ ์‚ฌ์šฉํ–ˆ๋˜ refactor ๋ฉ”๋‰ด๋ฅผ ์ด์šฉํ•  ๊ฒƒ์ธ๋ฐ, ์ด๋ฒˆ์—๋Š” extract ํ•˜์ง€ ์•Š๊ณ , Wrap with Padding์„ ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

 

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•๋ณด๋‹ค ๋‚ด๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž ๊น ์–ธ๊ธ‰ํ•ด๋ณด๋ ค ํ•œ๋‹ค.

refactor ๋ฉ”๋‰ด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ vscode ์ขŒ์ธก์— ์žˆ๋Š” ์ „๊ตฌ ์•„์ด์ฝ˜์„ ๋ˆ„๋ฅด๋Š” ๊ฒƒ์ด๋‹ค.

 

์ด๋ ‡๊ฒŒ ์ „๊ตฌ ์•„์ด์ฝ˜ -> Wrap with Padding ์„ ์„ ํƒํ•˜๋ฉด, ์ž๋™์œผ๋กœ Padding์ด ์œ„์ ฏ์„ ๊ฐ์‹ธ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , Padding ํฌ๊ธฐ๋ฅผ 8.0 -> 20 ์œผ๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.

 

์ด์ œ, ์œ„์—์„œ ๋งŒ๋“  Padding ์œ„์ ฏ์„ Card ์œ„์ ฏ์œผ๋กœ ๊ฐ์‹ธ์ค„ ๊ฒƒ์ด๋‹ค. ์ฆ‰, Padding ์œ„์ ฏ์˜ ์ƒ์œ„ ์œ„์ ฏ์œผ๋กœ Card ์œ„์ ฏ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

padding ์˜†์— ์žˆ๋Š” ์ „๊ตฌ ์•„์ด์ฝ˜์„ ๋ˆŒ๋Ÿฌ์„œ (์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค.) Wrap with widget...์„ ์„ ํƒํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ๋งŒ๋“ค์–ด์ง„ widget ํ…์ŠคํŠธ๋ฅผ ์ง€์šฐ๊ณ , Card๋ฅผ ์ž…๋ ฅํ•œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ๋นจ๊ฐ„ ์ค„๋„ ์‚ฌ๋ผ์งˆ ๊ฒƒ์ด๋‹ค !

 

์ค‘๊ฐ„์ ๊ฒ€์„ ํ•ด๋ณด์ž..!

์—ฌ๊ธฐ๊นŒ์ง€ ์ž˜ ๋”ฐ๋ผ ์™”๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™”๋ฉด์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

Theme and style

์ด์ œ ์นด๋“œ ์ƒ‰์„ ๋ฐ”๊ฟ”์ค„ ๊ฑด๋ฐ, ์ผ๊ด€๋œ ์ƒ‰ ๊ตฌ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ธฐ ๋•Œ๋ฌธ์—, ์•ฑ์˜ ํ…Œ๋งˆ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒ‰์„ ์„ ํƒํ•  ๊ฒƒ์ด๋‹ค.

main.dart์—์„œ BigCard ํด๋ž˜์Šค๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•œ๋‹ค.

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);         // ← Add this.

    return Card(
      color: theme.colorScheme.primary,    // ← And also this.
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Text(pair.asLowerCase),
      ),
    );
  }

// ...

์ด๋ ‡๊ฒŒ ์œ„์—์„œ ์ถ”๊ฐ€ํ•œ ๋‘ ์ฝ”๋“œ๋Š” ๊ต‰์žฅํžˆ ๋งŽ์€ ์ผ์„ ํ•œ๋‹ค.

  • ๋จผ์ €, ์ฒซ๋ฒˆ์งธ ์ฝ”๋“œ๋Š” ์•ฑ์˜ ํ˜„์žฌ ํ…Œ๋งˆ๋ฅผ Theme.of(context)๋กœ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
  • ๋‘ ๋ฒˆ์งธ ์ฝ”๋“œ๋Š” ํ…Œ๋งˆ์˜ colorScheme ์†์„ฑ๊ณผ ๋™์ผํ•˜๊ฒŒ ์นด๋“œ์˜ ์ƒ‰์ƒ์„ ์ •์˜ํ•œ๋‹ค. 
    • ์ƒ‰ ๊ตฌ์„ฑํ‘œ๋Š” ๋งŽ์€ ์ƒ‰์„ ํฌํ•จํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๊ธฐ๋ณธ ์ƒ‰์€ ์•ฑ์˜ primary color(์•ฑ์„ ์ •์˜ํ•˜๋Š”)์ด๋‹ค.

์ฆ‰, ์šฐ๋ฆฌ๋Š” ๋‘ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ MyApp ํด๋ž˜์Šค์—์„œ ์ •์˜ํ•œ theme color๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๊ฒƒ์ด๋‹ค.

๋งŒ์•ฝ, ์œ„์˜ seedColor๋ฅผ ๋ฐ”๊พธ๋ฉด, ํ•ด๋‹น ์นด๋“œ์˜ ์ƒ‰๋„ ๊ฐ™์ด ๋ฐ”๋€Œ๊ฒŒ ๋œ๋‹ค.

์ด๋ ‡๊ฒŒ theme color๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์•ฑ์˜ ์ „์ฒด์ ์ธ ๋ฉ”์ธ ์ƒ‰์„ ๋ฐ”๊ฟ”์•ผ ํ•  ๋•Œ, ๊ทธ ์ƒ‰์ด ์‚ฌ์šฉ๋œ ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜์”ฉ ์ฐพ์ง€ ์•Š๊ณ , MyApp์˜ theme์—์„œ ์ƒ‰๋งŒ ๋ฐ”๊ฟ”์ฃผ๋ฉด, ๋ชจ๋‘ ๋ฐ”๋€Œ๊ฒŒ ๋œ๋‹ค.

 

TextTheme

์ง€๊ธˆ๊นŒ์ง€ ์™„์„ฑํ•œ ํ™”๋ฉด์„ ๋ณด๋ฉด ์ด๋Ÿฌํ•˜๋‹ค.

ํ•˜์ง€๋งŒ, ์นด๋“œ ์•ˆ์— ์žˆ๋Š” ๊ธ€์”จ๊ฐ€ ๊ฒ€์€์ƒ‰์ด๋ฉฐ, ํฌ๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์ž‘์•„ ์ฝ๊ธฐ ํž˜๋“ค๋‹ค.

์ด์ œ BigCard์˜ build() ๋ฉ”์†Œ๋“œ๋ฅผ ๊ณ ์ณ์„œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด BigCard์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณด์ž

 

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);
    // ↓ Add this.
    var style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),
        // ↓ Change this line.
        child: Text(pair.asLowerCase, style: style),
      ),
    );
  }

// ...
  • theme.textTheme์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ app์˜ font theme์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” bodyMedium(medium size์˜ ํ…์ŠคํŠธ๋ฅผ ์œ„ํ•œ), caption(์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•œ), headlineLarge(large headlines๋ฅผ ์œ„ํ•œ)๊ณผ ๊ฐ™์€ ์†์„ฑ๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.
  • displayMedium property๋Š” ๊ณต์‹ ๋ฌธ์„œ์—, "์งง๊ณ  ์ค‘์š”ํ•œ ํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ" ๋ผ๊ณ  ๋‚˜์™€์žˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ์™€ ๊ฐ™๋‹ค !
  • displayMedium ๋Š” ์ด๋ก ์ ์œผ๋กœ null์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ Dart๋Š” null-safe์ด๊ธฐ ๋•Œ๋ฌธ์— null์ด ๋˜๋„๋ก ๋‚ด๋ฒ„๋ ค๋‘์ง€ ์•Š๋Š”๋‹ค.. ๊ทธ๋ž˜์„œ, ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ! ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ๋ญ˜ ํ•˜๊ณ  ์‹ถ์€์ง€ Dart๊ฐ€ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผ ํ•œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•œ ๊ฒƒ์€ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•˜์ง€ ์•Š๋Š”๋‹น..ใ…Žใ…Ž
  • displayMedium ์— copyWith() ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ ์ˆ˜์ •์‚ฌํ•ญ๊ณผ ํ•จ๊ป˜ ํ…์ŠคํŠธ ์Šคํƒ€์ผ์˜ ๋ณต์‚ฌ๋ณธ์ด return ๋œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ–ˆ๋˜ ๊ฒฝ์šฐ๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ์šฐ๋ฆฌ๋Š” ํ…์ŠคํŠธ ์ƒ‰์ƒ๋งŒ ๋ณ€๊ฒฝํ•œ ๊ฒƒ์ด๋‹ค.
  • ๋˜ ๋‹ค๋ฅธ ์ƒˆ๋กœ์šด ์ƒ‰์ƒ์„ ์–ป๊ณ  ์‹ถ๋‹ค๋ฉด, ์•ฑ์˜ ํ…Œ๋งˆ์— ๋‹ค์‹œ ์ ‘๊ทผํ•˜๋ฉด ๋œ๋‹ค. 

์ง€๊ธˆ๊นŒ์ง€ ํ•œ ๊ฒฐ๊ณผ์ด๋‹ค..!!

ํ›„... step 5... ์ƒ๊ฐ๋ณด๋‹ค ๊ธธ๋‹ค.....

ํ•˜์ง€๋งŒ...! ๋ฒŒ์จ 50%๋Š” ํ–ˆ์œผ๋‹ˆ.. ๊ดœ์ฐฎ๋‹ค....!!!!!!

๊ณ„์† ๊ฐ€๋ณด์ž๊ตฌ...

 

Improve accessibility

flutter๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ฑ์— ์—‘์„ธ์Šค ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด์„œ, flutter ์•ฑ์€ ์•ฑ์˜ ๋ชจ๋“  ํ…์ŠคํŠธ๋“ค์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ‘œ์‹œํ•ด์„œ TalkBack ๋ฐ VoiceOver์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

pair.asLowerCase๋ณด๋‹ค๋Š” upper camel case๊ฐ€ screen reader๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ๋‹จ์–ด๋ฅผ ์ธ์‹ํ•˜๋Š” ๊ฒƒ์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

์ž์„ธํ•œ ๋ถ€๋ถ„์€ ์ฝ”๋“œ๋žฉ์„ ๋ณด์‹œ๊ธธ...... (์ ˆ๋Œ€ ๊ท€์ฐฎ์•„์„œ ์ƒ๋žตํ•˜๋Š”๊ฒŒ ๋งž์Šต๋‹ˆ๋‹ค..ใ…Žใ…Ž)

 

๊ทธ๋ž˜์„œ..! ๊ฒฐ๊ตญ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณด์ž.

๊ทธ๋Ÿฌ๋ฉด, ์ด์ œ screen readers๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ฐ ๋‹จ์–ด ์Œ๋“ค์„ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. (๋ฌผ๋ก , UI์—๋Š” ๋ณ€ํ™”๊ฐ€ ์—†์„ ๊ฒƒ์ด๋‹ค!)

// ...

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);
    var style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),

        // ↓ Make the following change.
        child: Text(
          pair.asLowerCase,
          style: style,
          semanticsLabel: pair.asPascalCase,
        ),
      ),
    );
  }

// ...

๋ณ€ํ™”๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, screen reader๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ณด์‹œ๊ธธ...!!

 

 

Center the UI

์ด์ œ..!! UI์˜ ์„ผํ„ฐ๋ฅผ ๋งž์ถฐ์„œ ์‹œ๊ฐ์ ์ธ ๋ถˆํŽธํ•จ์„ ํ•ด์†Œํ•ด ๋ณผ ๊ฒƒ์ด๋‹ค.

๋จผ์ €, BigCard ๋Š” Column์˜ ๋ถ€๋ถ„์ž„์„ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค. Column์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž์‹๋“ค์„ top์œผ๋กœ ํ•œ๋ฒˆ์— ๋‘์ง€๋ฉด, ์ด๋ฅผ ์‰ฝ๊ฒŒ override ํ•  ์ˆ˜ ์žˆ๋‹ค.

MyHomePage์˜ build() ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ€์„œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ”๋ณด์ž..!

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,  // ← Add this.
        children: [
          Text('A random AWESOME idea:'),
          BigCard(pair: pair),
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, Column ์•ˆ์— ์žˆ๋Š” children๋“ค์ด main(vertical=์ˆ˜์ง) axis์— ๋งž์ถฐ center๊ฐ€ ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์‹ค Column ์•ˆ์— ์žˆ๋Š” children๋“ค์€ ์ด๋ฏธ main axis ๋ฐฉํ–ฅ์˜ center๊ฐ€ ๋งž์ถฐ์ ธ ์žˆ๋‹ค. ์ฆ‰, ๊ฐ€๋กœ ์„ผํ„ฐ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”๋ฐ, ์ ์šฉ๋˜์ง€ ์•Š์€ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

๊ทธ ์ด์œ ๋Š”, Column ์ž์ฒด๊ฐ€ ์„ผํ„ฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ, Column์˜ ์„ผํ„ฐ๋ฅผ ๋งž์ถฐ์ฃผ๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ์„ผํ„ฐ ์œ„์น˜๋กœ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค.

Column์—์„œ ์ „๊ตฌ ์•„์ด์ฝ˜์„ ๋ˆ„๋ฅด๊ฑฐ๋‚˜, cmd . ๋˜๋Š” ctrl . ์„ ๋ˆŒ๋Ÿฌ Refactor menu๋ฅผ ์—ด์–ด์ค€๋‹ค.

Refactor menu -> Wrap with Center

๊ทธ ๊ฒฐ๊ณผ, ํ™”๋ฉด์˜ ์„ผํ„ฐ์— ์œ„์น˜ํ•œ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค !!!

 

์ด์ œ ์กฐ๊ธˆ ๋””ํ…Œ์ผ์ ์ธ UI๋ฅผ ์ˆ˜์ •ํ•  ์ฐจ๋ก€์ด๋‹ค.

  • ์ผ๋‹จ, ์นด๋“œ ์œ„์— ์žˆ๋Š” "A random AWESOME idea:" ํ…์ŠคํŠธ๋Š” ์—†์• ๋„ ๋˜๋ฏ€๋กœ ์ง€์›Œ์ฃผ๊ฒ ๋‹ค.
  • ์นด๋“œ์™€ "Next" ๋ฒ„ํŠผ ์‚ฌ์ด์— ์—ฌ๋ฐฑ์ด ์žˆ์œผ๋ฉด ๋” ์ข‹์„ ๊ฒƒ ๊ฐ™์œผ๋ฏ€๋กœ, SizedBox ์œ„์ ฏ์„ ์‚ฌ์šฉํ•ด์„œ ๋‘ ์œ„์ ฏ ์‚ฌ์ด์— '๊ณต๊ฐ„'์„ ๋งŒ๋“ค์–ด์ค€๋‹ค

MyHomePage์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๋œ๋‹ค.

 

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                appState.getNext();
              },
              child: Text('Next'),
            ),
          ],
        ),
      ),
    );
  }
}

// ...

์ด์ œ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ์ฒ˜์Œ์— ๋ชฉํ‘œํ•œ ์ด๋ฏธ์ง€์™€ ์ผ์น˜ํ•˜๋‹ค !!!!!

 

์ด๋ ‡๊ฒŒ.. ๊ธธ์—ˆ๋˜ step5๊ฐ€ ๋๋‚ฌ๋‹ค..!!!

๊ทธ์ € ๋ณด๊ณ  ๋งŒ๋“ค๊ธฐ๋งŒ ํ–ˆ๋‹ค๋ฉด, ๋นจ๋ฆฌ ๋๋‚ฌ์„ํ…Œ์ง€๋งŒ, ์ดํ•ดํ•˜๊ณ , ์ด๋ฅผ ์ •๋ฆฌํ•˜๋‹ค๋ณด๋‹ˆ ๊ต‰์žฅํžˆ ๊ธธ๊ฒŒ ๋Š๊ปด์กŒ๋‹ค...

 

๊ทธ๋งŒ ์ ๊ณ  ์‹ถ์€ ์ˆœ๊ฐ„๋„ ๋งŽ์ด ์žˆ์—ˆ์ง€๋งŒ...!!!!

 

๊ทธ๋ž˜๋„ step 5 ๋ !!

 

์•„์ฐธ! ํ˜น์‹œ ๋ชฐ๋ผ ํ•„์š”ํ•œ ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ์ „์ฒด ์ฝ”๋“œ...!!

 

lib/main.dart

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }
}

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                appState.getNext();
              },
              child: Text('Next'),
            ),
          ],
        ),
      ),
    );
  }
}

class BigCard extends StatelessWidget {
  const BigCard({
    Key? key,
    required this.pair,
  }) : super(key: key);

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    var theme = Theme.of(context);
    var style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),

        // ↓ Make the following change.
        child: Text(
          pair.asLowerCase,
          style: style,
          semanticsLabel: pair.asPascalCase,
        ),
      ),
    );
  }
}

// ...
728x90
๋ฐ˜์‘ํ˜•