ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Flutter] ์ฒ˜์Œ Flutter ์ž…๋ฌธ์ž๋ผ๋ฉด ํ•œ๋ฒˆ ๋ณด๋ฉด ์ข‹์„ ๋จธํ„ฐ๋ฆฌ์–ผ ๊ตฌ์„ฑ์š”์†Œ ๊ธฐ๋ณธ์‚ฌํ•ญ ๐Ÿ˜„
    ๊ฐœ๋ฐœ/Flutter๐Ÿ“ฑ 2023. 8. 16. 20:26
    ์ตœ๊ทผ์— ๊ถ๊ธˆํ•œ ๋ถ€๋ถ„์ด ์žˆ์—ˆ๋Š”๋ฐ ๋ฌผ์–ด๋ณผ ๊ณณ์ด ์—†์–ด์„œ
    ์ฐพ๋‹ค๊ฐ€ Flutter์˜คํ”ˆ ์ฑ„ํŒ…๋ฐฉ์ด ์žˆ์–ด์„œ ๋“ค์–ด๊ฐ€์„œ ์ธ์‚ฌ๋ฅผ ํ–ˆ๋Š”๋ฐ..

    [Me] -"์•ˆ๋…•ํ•˜์„ธ์š”:) ํ”Œ๋ฃจํ„ฐ ๋ฐฐ์šด์ง€ 3๊ฐœ์›”์ •๋„ ๋ฌ์Šต๋‹ˆ๋‹คใ…Ž ์ž˜ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค."
    ๋ผ๊ณ  ํ–ˆ๋‹ค๊ฐ€...
    [๊ฐ€] - ํ”Œ๋Ÿฌ๋Ÿฌ์š”?
    [๋‚˜] - ํ”Œ๋Ÿฌํ„ฐ๋ณด๋‹จ ํ”Œ๋ŸฌํŒ…์ด..
    [๋‹ค] - ํ”Œ๋ฃจํ† ์š”?
    [๋ผ] - ํ”Œ๋ฃจํ„ฐ ํ–ˆ๋”๋‹ˆ ํ”Œ๋ฃจ์— ๊ฑธ๋ฆฐ๊ฑด๊ฐ€...
    .
    .
    .
    ์š”๋ ‡๊ฒŒ 10๋ถ„๋™์•ˆ ๋ง์žฅ๋‚œ์ด ์‹œ์ž‘๋˜์—ˆ๋‹ค๋Š”.. ๐Ÿฅฒ

     

     

    Material Design๋ž€?

    ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ๋””์ž์ธ์˜ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ง€์›ํ•˜๋Š” ์ ์‘ํ˜• ๊ฐ€์ด๋“œ๋ผ์ธ, ๊ตฌ์„ฑ์š”์†Œ ๋ฐ ๋„๊ตฌ ์‹œ์Šคํ…œ.

    ์˜คํ”ˆ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” Material Design์€ ๋””์ž์ด๋„ˆ์™€ ๊ฐœ๋ฐœ์ž ๊ฐ„์˜ ํ˜‘์—…์„ ๊ฐ„์†Œํ™”ํ•˜๊ณ  ํŒ€์ด ์•„๋ฆ„๋‹ค์šด ์ œํ’ˆ์„ ์‹ ์†ํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•จ.

     

    Material Flutter ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

    ์•ฑ๊ณผ ํ”Œ๋žซํผ ์ „๋ฐ˜์— ๊ฑธ์ณ ์ผ๊ด€์ ์ธ ์‚ฌ์šฉ์ž ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Material Design ๊ตฌ์„ฑ์š”์†Œ (์ค„์—ฌ์„œ MDC)์˜ ๋””์ž์ธ์„ ๊ตฌํ˜„ํ•˜๋Š” Flutter ์œ„์ ฏ์ด ํฌํ•จ๋˜์–ด ์žˆ์Œ.

     


    ์‹œ์ž‘ํ•˜๊ธฐ์ „.

    github์—์„œ ์ผ๋ถ€ ์†Œ์Šค ๋‹ค์šด ๐Ÿ˜„

    git clone https://github.com/material-components/material-components-flutter-codelabs.git
    cd material-components-flutter-codelabs/mdc_100_series
    git checkout 101-starter

    MDC-101 ๋ชฉํ‘œ  : ๋กœ๊ทธ์ธ ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ.

     

    1. ์ด๋ฉ”์ผ, ํŒจ์Šค์›Œ๋“œ ์ž…๋ ฅํ•  Textfield ๋งŒ๋“ค๊ธฐ.

    TextField(
      decoration: const InputDecoration(
        filled: true, //<- ๋ฐ”ํƒ•์ƒ‰
        labelText: 'Username',
      ),
    ),
    // spacer
    const SizedBox(height: 12.0),
    // [Password]
    TextField(
      decoration: const InputDecoration(
        filled: true, 
        labelText: 'Password',
      ),
      obscureText: true,
    ),

     

    2.Textfield ์•ˆ์— TextEditingController ๋„ฃ๊ธฐ.

    final _usernameController = TextEditingController();
    final _passwordController = TextEditingController();
    TextField(
           controller: _usernameController,
           decoration: const InputDecoration(
           filled: false,
           labelText: 'Username',
             ),
           ), 
    const SizedBox(height: 12.0), 
    TextField(
           controller: _passwordController,
           decoration: const InputDecoration(
           filled: true,
           labelText: 'Password',
             ),
           obscureText: true,
     )

     

    โ—๏ธTextfield์œ„์ ฏ๊ณผ ์„ธํŠธ๋กœ ์•Œ์•„์•ผ ํ•  ์ปจํŠธ๋กค๋Ÿฌ!

    TextEditingController

    : ํŽธ์ง‘์ด ๊ฐ€๋Šฅํ•œ TextField์— ์ž…๋ ฅ๋œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์˜ค๊ฑฐ๋‚˜ ์ž…๋ ฅ๋œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค

     

    ๊ธฐ๋Šฅ

    • clear() : ์ž…๋ ฅํ•œ ํ…์ŠคํŠธ ๋ชจ๋‘ ์‚ญ์ œ 
    editingController.clear();
    • text : ํ˜„์žฌ ์ž…๋ ฅ๋œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ (string)
    editingController.text
    • addListener: ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ์ด ๋ ๋•Œ๋งˆ๋‹ค ์•Œ๋ ค์คŒ.
    editingController.addListener({function});
    • dispose(): ๋ฆฌ์†Œ์Šค ํ•ด์ œ.
    editingController.dispose();

     


    ๐Ÿ’กํ•จ๊ป˜ ์•Œ์•„๋‘๋ฉด ์ข‹์„ InputDecoration

    InputDecoration์„ ์‚ฌ์šฉํ•˜๋ฉด Textfield ์œ„์ ฏ์„ ์ข€ ๋” ๋‹ค์–‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.

    TextField(
      decoration: InputDecoration(
        labelText: 'Email', //<- ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ
        hintText: 'Enter your email', //<- ํžŒํŠธ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ํ…์ŠคํŠธ
        labelStyle: TextStyle(color: Colors.redAccent), 
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.all(Radius.circular(10.0)),
          borderSide: BorderSide(width: 1, color: Colors.redAccent),
        ),
        enabledBorder: OutlineInputBorder( //<- ํฌ์ปค์Šค๋ซ์„ ๋•Œ 
          borderRadius: BorderRadius.all(Radius.circular(10.0)),
          borderSide: BorderSide(width: 1, color: Colors.redAccent),
        ),
        border: OutlineInputBorder( //<- ๊ธฐ๋ณธ ๋ชจ์–‘
          borderRadius: BorderRadius.all(Radius.circular(10.0)), 
        ),
      ),
      keyboardType: TextInputType.emailAddress,

     


    3. ๋ฒ„ํŠผ ์ถ”๊ฐ€

    ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ํ†ตํ•ด ์ง„ํ–‰ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž„๋•Œ๋Š” ElevatedButton, ์‚ฌ์šฉ์ž๊ฐ€ ํ•˜์ง€ ์•Š๊ธฐ๋ฅผ ๋ฐ”๋ž„ ๋•Œ๋Š” TextButton ์ง€ํ–ฅ.

    (์ด์™ธ์—๋„ OutlinedButton, FloatingActionButton IconButton ๋“ฑ์ด ์žˆ์Œ.)

    ๋ฒ„ํŠผ์˜ ๋ชจ์–‘

     

     OverflowBar(
          alignment: MainAxisAlignment.end,
          // TODO: Add a beveled rectangular border to CANCEL (103)
          children: <Widget>[
          // TODO: Add buttons (101)
                TextButton(
                   child: const Text('CANCEL'),
                   onPressed: () {
                     // TODO: Clear the text fields (101)
                      _usernameController.clear();
                      _passwordController.clear();
                    },
                 ),
                 // TODO: Add an elevation to NEXT (103)
                 // TODO: Add a beveled rectangular border to NEXT (103)
                 ElevatedButton(
                    child: const Text('NEXT'),
                    onPressed: () {
                        // TODO: Show the next page (101)
                       Navigator.pop(context);
                     },
                  ),
             ],
        )

     


    ๐Ÿ’กํ•จ๊ป˜ ์•Œ์•„๋‘๋ฉด ์ข‹์„ OverflowBar

    : ์ž์‹๋ทฐ๋ฅผ ์ •๋ ฌ์‹œํ‚ด.

    class MSOverflowBarRoute extends StatelessWidget {
      const MSOverflowBarRoute({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text("MSOverflowBarRoute")),
          body: Column(
            children: [
              OverflowBar(
                alignment: MainAxisAlignment.end,  
                spacing: 20.0,  
                textDirection: TextDirection.ltr,  
                overflowSpacing: 20.0,  
                overflowAlignment: OverflowBarAlignment.center,  
                overflowDirection:
                    VerticalDirection.down,  
                clipBehavior: Clip.hardEdge,  
                 
                children: [
                  ElevatedButton(onPressed: () {}, child: Text("1XXXXXXXXXXX1")),
                  ElevatedButton(onPressed: () {}, child: Text("1YYYYYYYYYYY1")),
                ],
              ),
              OverflowBar(
                alignment: MainAxisAlignment.spaceAround,  
                spacing: 20.0, 
                textDirection: TextDirection.rtl, 
                overflowSpacing: 20.0,  
                overflowAlignment: OverflowBarAlignment.center,  
                overflowDirection:
                    VerticalDirection.down,  
                clipBehavior: Clip.hardEdge,  
          
                children: [
                  ElevatedButton(onPressed: () {}, child: Text("2XXXXXXXXXXX2")),
                  ElevatedButton(onPressed: () {}, child: Text("2YYYYYYYYYYY2")),
                ],
              ),
              OverflowBar(
                alignment: MainAxisAlignment.end, 
                spacing: 20.0,
                textDirection: TextDirection.ltr, 
                overflowSpacing: 10.0,
                overflowAlignment: OverflowBarAlignment.center, 
                overflowDirection:
                    VerticalDirection.down, 
                clipBehavior: Clip.none,  
                children: [
                  ElevatedButton(onPressed: () {}, child: Text("3XXXXXXXXXXX3")),
                  ElevatedButton(onPressed: () {}, child: Text("3YYYYYYYYYYY3")),
                  ElevatedButton(onPressed: () {}, child: Text("3ZZZZZZZZZZZ3")),
                ],
              ),
              OverflowBar(
                alignment: MainAxisAlignment.end,
                spacing: 20.0, 
                textDirection: TextDirection.ltr, 
                overflowSpacing: 10.0,
                overflowAlignment: OverflowBarAlignment.end, 
                overflowDirection:
                    VerticalDirection.up, 
                clipBehavior: Clip.none,
                children: [
                  ElevatedButton(onPressed: () {}, child: Text("4XXXXXXXXXXX4")),
                  ElevatedButton(onPressed: () {}, child: Text("4YYYYYYYYYYY4")),
                  ElevatedButton(onPressed: () {}, child: Text("4ZZZZZZZZZZZ4")),
                ],
              ),
            ],
          ),
        );
      }
    }

    MDC-102 ๋ชฉํ‘œ : ์ƒํ’ˆ ๋ชฉ๋ก ๋ฆฌ์ŠคํŠธ ๋งŒ๋“ค๊ธฐ

     

    1. ์ƒ๋‹จ์— ์•ฑ๋ฐ” ์ถ”๊ฐ€

     

     

     

     

     

    return Scaffold(
          // TODO: Add app bar (102)
          appBar: AppBar(
            title: Text('SHRINE'), //
            leading: IconButton(
              icon: const Icon(
                Icons.menu,
                semanticLabel: 'menu',
              ),
              onPressed: () {
                print('Menu button');
              },
            ),
            actions: <Widget>[
              IconButton(
                icon: const Icon(
                  Icons.search,
                  semanticLabel: 'search',
                ),
                onPressed: () {
                  print('Search button');
                },
              ),
              IconButton(
                icon: const Icon(
                  Icons.tune,
                  semanticLabel: 'filter',
                ),
                onPressed: () {
                  print('Filter button');
                },
              ),
            ],
          ),
    			body: Center(
            child: Text('You did it!'),
          ),
          // TODO: Set resizeToAvoidBottomInset (101)
        );

    2. ์ƒํ’ˆ ๋ชฉ๋ก ๋งŒ๋“ค๊ธฐ (GridView ์‚ฌ์šฉ)

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    ์ƒํ’ˆ ๋ณด์—ฌ์ค„ Card์ถ”๊ฐ€ (์ƒํ’ˆ ์ •๋ณด๋Š” ๋‹ค ์˜ˆ์ œ ํŒŒ์ผ ์•ˆ์— ๋“ค์–ด ์žˆ์Œ.)

    List<Card> _buildGridCards(BuildContext context) {
        List<Product> products = ProductsRepository.loadProducts(Category.all);
    
        if (products.isEmpty) {
          return const <Card>[];
        }
    
        final ThemeData theme = Theme.of(context);
        final NumberFormat formatter = NumberFormat.simpleCurrency(locale: Localizations.localeOf(context).toString());
    
        return products.map((product) {
          return Card(
            clipBehavior: Clip.antiAlias,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                AspectRatio(aspectRatio: 18/11,
                child: Image.asset(product.assetName,
                package: product.assetPackage,),),
                Expanded(child: Padding(
                  padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        product.name,
                        style: theme.textTheme.titleLarge,
                        maxLines: 1,
                      ),
                      const SizedBox(height: 8.0,),
                      Text(
                        formatter.format(product.price),
                        style: theme.textTheme.titleSmall,
                      )
                    ],
                  ),
                ))
              ],
            ),
          );
        }
        ).toList();
    
      }

    GridView์•ˆ์— ์นด๋“œ ์ถ”๊ฐ€.

    return Scaffold(
          // TODO: Add app bar (102)
          appBar: AppBar(
            title: Text('SHRINE'),
            leading: IconButton(
              icon: const Icon(
                Icons.menu,
                semanticLabel: 'menu',
              ),
              onPressed: () {
                print('Menu button');
              },
            ),
            actions: <Widget>[
              IconButton(
                icon: const Icon(
                  Icons.search,
                  semanticLabel: 'search',
                ),
                onPressed: () {
                  print('Search button');
                },
              ),
              IconButton(
                icon: const Icon(
                  Icons.tune,
                  semanticLabel: 'filter',
                ),
                onPressed: () {
                  print('Filter button');
                },
              ),
            ],
          ),
          // TODO: Add a grid view (102)
          body: GridView.count(crossAxisCount: 2,
          padding: const EdgeInsets.all(16),
          childAspectRatio: 8.0/9.0,
          children: _buildGridCards(context),)
          // TODO: Set resizeToAvoidBottomInset (101)
        );
      }

     

    ์—ฌ๊ธฐ๊นŒ์ง€ ํ–ˆ๋‹ค๋ฉด ๊ธฐ๋ณธ์€ ๋‹ค ์•„๋Š” ๊ฒƒ! ใ…Ž

     

     

    [์ „์ฒด ์†Œ์Šค]

    : git@github.com:AlimE1789/materialComponents_101_102.git

     

     

    ๋ฐ˜์‘ํ˜•
Designed by Tistory.