route는 하나의 페이지라고 생각하면 된다.

이번에는 다른 파일의 페이지를 여는 방식을 살펴볼 것이다. Navigator1에서 다룬 내용은 싱글 페이지에서 페이지 연결하는 방식? 이라 생각됨(아직은 잘 몰라서 ..)

MaterialApp에서 home을 사용하는게 아니라 initialRoute와 routes를 이용해서 페이지 구조를 연결함

Navigator의 pushNamed를 사용

즉, home과는 다른 방식

4개의 파일 구조는 아래와 같다.

 

main. dart

import 'package:flutter/material.dart';
import 'package:flutter_1229/ScreenA.dart';
import 'package:flutter_1229/ScreenB.dart';
import 'package:flutter_1229/ScreenC.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute:'/',
      routes: {
        '/' : (context) => ScreenA(),
        '/b' : (context) => ScreenB(),
        '/c' : (context) => ScreenC(),
      },
    );
  }
}

 

 

ScreenA.dart

import 'package:flutter/material.dart';

class ScreenA extends StatelessWidget {
  const ScreenA({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('ScreenA')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/b');
                },
                child: Text('Go to ScreenB')),
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/c');
                },
                child: Text('Go to ScreenC'))
          ],
        ),
      ),
    );
  }
}

 

ScreenB.dart

import 'package:flutter/material.dart';

class ScreenB extends StatelessWidget {
  const ScreenB({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenB"),
        ),
        body: Center(
          child: Text(
            'ScreenB',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

ScreenC.dart

import 'package:flutter/material.dart';

class ScreenC extends StatelessWidget {
  const ScreenC({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenC"),
        ),
        body: Center(
          child: Text(
            'ScreenC',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

 

 

 

 

 

* 우선 22~23강의 route개념은 중요하므로 여러번 봐야할 듯 , 그리고 24강 끝에 해답이있는데 이것이 핵심? ㅜㅜ

1. Route는 페이지라 생각 하면 됨

2. Navigator는 push, pop을 사용하며 stack형태를 이룸

3. push할 때 MaterialPageRoute(builder)를 사용함 *

  - BuildContext정보가 중요함

** 매우 중요한 것 MaterialPageRoute에서 context 인자로 넘겨줄 때 MaterialApp의 상위 위젯의 context를 넘겨주면 안된다. 그렇기에 아래 push처럼 MaterialApp의 하위 child의 context를 넘겨줘야함.

(아래 코드들에서 첫 route는 First이고 두번째 route는 Second임)

push 핵심 코드

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('First'),
      centerTitle: true,
    ),
    body: Center(
      child: ElevatedButton(
        child: Text('Go to the Second Page'),
      onPressed: () {
        Navigator.push(context,
          MaterialPageRoute(builder: (BuildContext context) {
          return Second();
        }));
      },
    )),
  );
}

만약 builder의 매개변수를 사용하지 않을 경우 아래처럼 _ 를 사용하는 것을 추천함

onPressed: () {
  Navigator.push(context,
    MaterialPageRoute(builder: (_) => Second()
  ));
},

 

pop 핵심 코드

Widget build(BuildContext context2) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Second'),
      centerTitle: true,
    ),
    body: Center(
        child: ElevatedButton(
      child: Text('Go to the First Page'),
      onPressed: () {
        Navigator.pop(context2);
      },
    )),
  );
}

 

 

전체 코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Toast Message',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: First(),
    );
  }
}

class First extends StatelessWidget {
  const First({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the Second Page'),
        onPressed: () {
          Navigator.push(context,
            MaterialPageRoute(builder: (BuildContext context) {
            return Second();
          }));
        },
      )),
    );
  }
}

class Second extends StatelessWidget {
  const Second({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context2) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the First Page'),
        onPressed: () {
          Navigator.pop(context2);
        },
      )),
    );
  }
}

 

추천 사이트: Flutter Layout Cheat Sheet. Do you need simple layout samples for… | by Tomek Polański | Flutter Community | Medium

1. Column

- 아래처럼 새로로 배치할 때 사용하며, 중요한 건 컬럼은 아래 공간까지 다 차지한다는 것!

- 정렬방법1. Center위젯으로 감싸면 아래처럼 되고

- 정렬방법2. Column에 있는 위젯들을 세로로 정렬할 때는 아래와 같은 속성을 사용할 수 있다.

mainAxisAlignment: MainAxisAlignment.spaceEvenly

- 정렬방법3. 조금 중요한데 요소들을 오른쪽 끝에 붙이고 싶으면?

crossAxisAlignment: CrossAxisAlignment.end,

위와 같이 명령어를 써도 실제로는 동작하지 않는다.. 왜그럴까? 일단 동작하는 건 맞다. 하지만 가로 폭이 요소폭이랑 같아서 변화가 없는 것

아래와 같은 invinsible Container를 사용하면 된다.

Container(
  width: double.infinity,
)

 

 

 

 

 

 

 

 

 

사용 결과

 

- 정렬방법4. 컨테이너를 가로로 꽉 채우고 싶으면? 아래와 같이 주고 각 컨테이너의 폭width를 다 없애준다.

crossAxisAlignment: CrossAxisAlignment.stretch,

- 정렬방법5. 박스간의 간격을 조정하고 싶을 때는 SizedBox를 넣어서 간격 조절하기

 

 

2. Row는 Column과 반대 개념

 

뭐가 문제인지 모르겠지만.. 나오긴하나 모양이 안바뀜 ㅜㅜ 텍스트는 잘 바뀐다.. 이유는 천천히 찾아보자.

 

우선 fluttertoast는 pub에서 패키지를 받아서 구현할 수 있다.

fluttertoast | Flutter Package (pub.dev)

 

fluttertoast | Flutter Package

Toast Library for Flutter, Easily create toast messages in single line of code

pub.dev

pubspec.yaml의 flutter에 패키지를 등록해주면 import가 가능하다

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Toast Message',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Toast Message'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
              onPressed: () {
                flutterToast();
              },
              child: Text('Toast'),
              style: TextButton.styleFrom(
                backgroundColor: Colors.greenAccent
              ))),
    );
  }
}

void flutterToast() {
  Fluttertoast.showToast(
      msg: "This is Center Short Toast",
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
      fontSize: 16.0
  );
}

 

 

BuildContext개념은 강좌 확인

 

변경된 사용법

onPressed: () {
  ScaffoldMessenger.of(context)
      .showSnackBar(SnackBar(content: Text('Hello')));
}),

참고

https://yj95.tistory.com/364

 

import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Snack Bar'),
          centerTitle: true,
        ),
        body: Center(
          child: TextButton(
              child: Text(
                'show Me',
                style: TextStyle(
                  color: Colors.white,
                  backgroundColor: Colors.red,
                ),
              ),
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    backgroundColor: Colors.teal,
                    duration: Duration(milliseconds: 1000),
                    content: Text(
                      'Hello',
                      textAlign: TextAlign.center,
                      style: TextStyle(color: Colors.white),
                    )));
              }),
        ));
  }
}

App바 기본

1. leading : 아이콘 버튼이나 간단한 위젯을 왼쪽에 배치할 때

2. actions : 복수의 아이콘 버튼 등을 오른쪽에 배치했을 때

3. onPressed : 함수형태로 버튼이나 아이콘 터치했을 때 발생하는 이벤트를 정의하는 곳

 

코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'AppBar',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Appbar icon Menu'),
        centerTitle: true,
        elevation: 0.0,
        leading: IconButton(
          icon: Icon(Icons.menu), onPressed: () {
            print('menu button is clicked');
          },
        ),
        actions: [
          IconButton(
            icon: Icon(Icons.shopping_cart), onPressed: () {
            print('shp cart button is clicked');
          },
          ),
          IconButton(
            icon: Icon(Icons.search), onPressed: () {
            print('search button is clicked');
          },
          ),
        ],
      ),
    );
  }
}

 

drawer메뉴

 

기본 구성

 

 

* onTab, onPressed 차이

 

onPressed는 버튼에 주로 사용

 

onTab은 gestureDetector, InkWell 같은데 주로 사용

  - 길게 누르기, 두번 탭하기 등 액션 감지 가능

 

drawer코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'AppBar',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Appbar icon Menu'),
        centerTitle: true,
        elevation: 0.0,
        actions: [
          IconButton(
            icon: Icon(Icons.shopping_cart), onPressed: () {
            print('shp cart button is clicked');
          },
          ),
          IconButton(
            icon: Icon(Icons.search), onPressed: () {
            print('search button is clicked');
          },
          ),
        ],
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            UserAccountsDrawerHeader(
              currentAccountPicture: CircleAvatar(
                backgroundImage: AssetImage('1.png'),
                backgroundColor: Colors.white,
              ),
              otherAccountsPictures: [
                CircleAvatar(
                  backgroundColor: Colors.white,
                  backgroundImage: AssetImage('1.png'),
                ),
                // CircleAvatar(
                //   backgroundColor: Colors.white,
                //   backgroundImage: AssetImage('1.png'),
                // ),
              ],
              accountName: Text('changPa'),
              accountEmail: Text('changPa@chang.com'),
              onDetailsPressed: (){
                print('arrow is clicked');
              },
              decoration: BoxDecoration(
                color: Colors.red[200],
                borderRadius: BorderRadius.only(
                  bottomLeft: Radius.circular(20.0),
                  bottomRight: Radius.circular(20.0),
                )
              ),
            ),
            ListTile(
              leading: Icon(Icons.home,
              color: Colors.grey[850]),
              title: Text('Home'),
              onTap: (){
                print('Home is clicked');
              },
              trailing: Icon(Icons.add),
            ),
            ListTile(
              leading: Icon(Icons.settings,
              color: Colors.grey[850]),
              title: Text('Settings'),
              onTap: (){
                print('Settings is clicked');
              },
              trailing: Icon(Icons.add),
            ),
            ListTile(
              leading: Icon(Icons.question_answer,
              color: Colors.grey[850]),
              title: Text('Q&A'),
              onTap: (){
                print('Q&A is clicked');
              },
              trailing: Icon(Icons.add),
            ),

          ],
        ),
      ),
    );
  }
}

 

- 모든 변수는 기본적으로 non-nullable임, 즉 null을 할당 할 수 없음

- 모든 변수는 non-nullable이므로 null check가 필요없음(과거 if문으로 null일 경우 이렇게 해라 등)

- class 내의 변수 역시 non-nullable이므로 초기화가 필요하다.

 

 

1. Nullable type

 - Null이 올 수도 있다.

 - type뒤에 ? 표기

 

2. late 키워드

 - 잠시 후 초기화 할 거다.

 - type 앞에 late 표기

 

3. Exclamation or Bang   - !

 - 원래 Nullable변수는 non-Nullable변수에 대입할 수 없지만 이 변수는 절대 null 값을 가지지 않는 다는 것을 명시해서 대입가능하게 함

 - 대입하려는 변수 뒤에 ! 표기

 

4. named argument 형식에서 사용 (우리가 사용하는 Scaffold, Center등의 클래스가 named argument형식)

void main(){
  print(add(a:4, b:2));
}

int add({required int a, required int b}){
  int sum = a+b;
  return sum;
}

named argument는 선택적이라 null이 할당될 우려가 있음.

그래서 required 키워드를 붙여서 무조건 매개변수 값을 넘겨주도록 하거나

아니면 Nullable Type( ? )으로 변수를 선언하는 방법으로 회피하는데 함수 내에서 여전히 null값으로 남게되면 if문을 사용해서 null체크하라는 에러메시지를 띄운다.

 

5. lazy initialization (late 키워드 보충 - late 변수는 언제 초기화 되나?) 

class Person{
  late int age = calculation();
  void printAge(){
    print('age');
  }
}

int calculation(){
  print('calculate');
  return 30;
}

void main(){
  Person p = Person();
  p.printAge();
  print(p.age);
}

만약 age변수가 late키워드가 없었다면 출력은

calculate -> age -> 30으로 출력 될 것이다.

하지만 late키워드가 있기 때문에

age -> calculate -> 30 순으로 출력된다.

여기서 우리는 late키워드가 있으면 해당 변수(여기서는 age)가 참조될 때 실행된다는 것을 알 수 있다.

 

6. 이전버전 null-safety버전으로 마이그레이팅하기

https://www.youtube.com/watch?v=SjJ6pxYuqwg&list=PLQt_pzi-LLfpx8x6YEMvUwfJHZIEk2L6J&index=3

 

출처: https://www.youtube.com/watch?v=QP0THWoDeag&list=PLQt_pzi-LLfpx8x6YEMvUwfJHZIEk2L6J&index=2

[완성 화면]

[주요 개념]

- 이미지 import

- 위젯간의 간격은 Sizedbox로 조정

- padding

padding: EdgeInsets.fromLTRB(30, 40, 0, 0)

- Debug표시 없애기

debugShowCheckedModeBanner: false,

- 써클 아바타

CircleAvatar(
  backgroundImage: AssetImage('assets/charizard-mega-y.png'),
  radius: 60,
  backgroundColor: Colors.amber[600],
)

 

[완성 전체 코드]

import 'package:flutter/material.dart';
import 'package:mild_5/1.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'BBANTO',
      home: Grade()
    );
  }
}

class Grade extends StatelessWidget {
  const Grade({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amber[600],
      appBar: AppBar(
        title: Text('BBANTO'),
        backgroundColor: Colors.amber[700],
        centerTitle: true,
        elevation: 0,
      ),
      body: Padding(
        padding: EdgeInsets.fromLTRB(30, 40, 0, 0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Center(
              child: CircleAvatar(
                backgroundImage: AssetImage('assets/charizard.gif'),
                radius: 60.0,
                backgroundColor: Colors.black,
              ),
            ),
            Divider(
              height: 60.0, // 가로선 기준 위아래 간격이 30씩
              color: Colors.grey[850],
              thickness: 1,
              endIndent: 30.0,
            ),
            Text('Name',
            style: TextStyle(
              color: Colors.white,
              letterSpacing: 2.0,
            ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text('BBANTO',
            style: TextStyle(
              color: Colors.white,
              letterSpacing: 2,
              fontSize: 28,
              fontWeight: FontWeight.bold
            ),),
            SizedBox(
              height: 30,
            ),
            Text('BBANTO POWER LEVEL',
              style: TextStyle(
                color: Colors.white,
                letterSpacing: 2.0,
              ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text('14',
              style: TextStyle(
                  color: Colors.white,
                  letterSpacing: 2,
                  fontSize: 28,
                  fontWeight: FontWeight.bold
              ),),
            SizedBox(
              height:25
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('using lighsaber',
                  style: TextStyle(
                    fontSize: 16,
                    letterSpacing: 1
                  ),
                )
              ],
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('face hero tattoo',
                  style: TextStyle(
                      fontSize: 16,
                      letterSpacing: 1
                  ),
                )
              ],
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('fire flames',
                  style: TextStyle(
                      fontSize: 16,
                      letterSpacing: 1
                  ),
                )
              ],
            ),
            Center(
              child: CircleAvatar(
                backgroundImage: AssetImage('assets/charizard-mega-y.png'),
                radius: 60,
                backgroundColor: Colors.amber[600],
              )
            )
          ],
        ),
      ),

    );
  }
}

+ Recent posts