flutter MaterialApp을 활용하여 앱 요소 설정하기
Flutter 시작
Flutter을 시작하고 프로젝트를 생성하면 가장먼저 보게 되는 것이 MaterialApp이다.
기본예제로 아래와 같이 생성되며 runApp에 루트로 쓰이는 최상단 위젯이다.
html으로 비유하자면 <html></html>
태그와 같다고 볼 수 있다.
밑에 코드는 flutter 프로젝트 생성시 자동으로 생성되는 기본코드이다
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
App Style
flutter에서는 Android Style, IOS Style의 위젯을 지원한다
flutter 기본적으로 Android Style 기반인 Material UI를 기반으로 하며
IOS Style 기반으로 만들고 표현하고 싶은 경우 cupertino 위젯을 사용 할 수 있도록 지원한다
해당 위젯들은 다음과 같은 라이브러리를 import를 해야 사용 할 수 있다
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
MaterialApp
MaterialApp
은 구글이 지향하는 Material Design을 지양 할 수 있게 해주는 클래스로, main.dart
에 위치하며 Navigator 라우팅, 공통Theme와 같은 코드를 분리 할 수 있도록 해준다. 이런 MaterialApp
에 짝꿍처럼 따라다니는 클래스가 있는데 바로 Scaffold
이다. Scaffold는 사전에 찾아보면 처형대, 조립식 발판의 의미를 가지고 있다. html으로 따지면 <body></body>
태그이다.
라우팅
home속성을 사용
main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Home'),
),
),
),
);
}
중간에 home이라는 텍스트가 써지는 간단한 코드이다.
home속성으로 처음에 띄워지는 메인 스크린을 정의 할 수 있다. 하지만 여러개의 화면이 필요한 경우는 여러화면을 정의 할 수 있는 라우팅 기능이 필요하다.
routes속성을 사용
screen/home_screen.dart
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Home',
),
ElevatedButton(
onPressed: () {
/*Navigator.push(context,
MaterialPageRoute(builder: (context) => AboutScreen()));*/
Navigator.pushNamed(context, '/about');
},
child: Text('To About Page'))
],
))
);
}
}
screen/about_screen.dart
import 'package:flutter/material.dart';
class AboutScreen extends StatelessWidget {
const AboutScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('About'),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back'))
],
),
)
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:material_tutorial/screen/about_screen.dart';
import 'package:material_tutorial/screen/home_screen.dart';
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/about': (context) => const AboutScreen()
},
));
}
routes속성을 사용하여 맵 형태로 라우팅을 할 수 있다. url path을 지정해주는거처럼 라우트를 관리 할 수 있다.
앱을 만들다고 하면 여러 스크린이 필요한 경우가 대부분이기 때문에 위와 같이 관리한다.
Navigator
를 사용하면 라우트 스크린간 이동이 가능해지는데
Navigator.push(context, MaterialPageRoute(builder: (context) => AboutScreen()));
이런식으로 이동하고 싶은 스크린을 불러와서 push를 한다.
만약 AboutScreen을 다른 스크린으로 변경하고 싶다면, 해당 코드들을 다 찾아서 고쳐야 될 것이다.
이러한 문제점을 해결하기 위해 라우트가 필요한 것이다.;
Navigator.pushNamed(context, '/about');
pushNamed
메소드를 통해 url path을 입력해주면 간단하게 스크린을 이동 할 수 있다.
Navigator 홈화면을 띄워주는 것에 대한 우선순위 과정
/
경로인 경우 또는 home 속성이null
이 아닌 경우 사용routes
테이블이 정의되어 있을 경우 첫번째 요소를 사용onGenerateRoute
가 불렸을 때null
이 아닌 유효한 home, routes을 반환하는것을 사용- 위 모든 경우가 실패했을때
onUnknownRoute
가 호출된다.
공통 테마 및 텍스트 설정
screen/home_screen.dart
import 'package:flutter/material.dart';
import 'package:material_tutorial/screen/about_screen.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
centerTitle: true,
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Home',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w700),
),
ElevatedButton(
onPressed: () {
/*Navigator.push(context,
MaterialPageRoute(builder: (context) => AboutScreen()));*/
Navigator.pushNamed(context, '/about');
},
child: Text('To About Page'))
],
)),
);
}
}
screen/about_screen.dart
import 'package:flutter/material.dart';
class AboutScreen extends StatelessWidget {
const AboutScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('About'),
// backgroundColor: Colors.deepOrange,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('About'),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Back'))
],
),
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:material_tutorial/screen/about_screen.dart';
import 'package:material_tutorial/screen/home_screen.dart';
void main() {
runApp(MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/about': (context) => const AboutScreen()
},
theme: ThemeData(
primarySwatch: Colors.deepOrange,
brightness: Brightness.light,
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: Colors.lightBlueAccent),
appBarTheme: AppBarTheme(
backgroundColor: Colors.lightBlueAccent, centerTitle: true),
fontFamily: 'dancingscript',
textTheme: const TextTheme(
headline1: TextStyle(fontSize: 30),
headline2: TextStyle(fontSize: 25),
bodyText1: TextStyle(fontSize: 20))),
debugShowCheckedModeBanner: false,
));
}
theme라는 속성에 ThemeData
를 줌으로써 공통 테마를 정의 할 수 있다.Scaffold
에는 body뿐만아니라 화면을 보조 해줄 수 있는 Appbar
, FloatingActionButton
등 다양한 위젯을 지원한다.
이러한 색깔의 기본 색깔은 파랑색인데 위젯의 color속성 또는 backgroundColor속성으로 직접적으로 색깔을 지정해서 바꿔줄수 있다.
하지만 매 스크린마다 각 요소들에 색깔을 지정하는일은 코드가 길어지고 primaryColor를 변화하고 싶을 경우 하나하나 변경해줘야 되는 문제점이 생긴다.
이러한 문제를 해결하기 위해 ThemeData를 사용하는 것이다. primarySwatch에 primaryColor를 지정해주면, 기본 제공 위젯들의 Default Color가 변화하는 것을 볼 수가 있다. 그 외에도 brightness를 통해 light모드, dark모드를 지정 해줄 수 있으며, 각 모드별로 메인 색상을 지정 할 수도 있다. AppBarTheme
같이 개별적인 위젯을 대상으로도 테마를 지정 해줄 수 있다.
또한 Theme.of(context).colorScheme.primary
이런식으로 Primary Color를 불러와 직접적으로 사용 할수도 있다.
이런식으로 테마를 잘 활용하면은 앱을 개발 할때 불필요하게 반복되는 코드를 줄 일 수 있으며, constant/color.dart
라는 파일을 만들어서 자주 쓰는 색상코드를 정의 해서 쓴다던지, util/style.dart
자주쓰는 스타일을 정의해 줄 수있다.
fontFamily
와 TextTheme
도 지정하여 공통된 글꼴과 텍스트 테마를 지정해줄 수 있는데,
Google Fonts: Dancing Script
Dancing Script is a lively casual script where the letters bounce and change size slightly. Caps are big, and goes below the baseline. Dancing Script references
fonts.google.com
해당 사이트에서 받은 폰트를 /asset/font/경로의 폴더를 만들어 넣고
pubspec.yaml에 다음과 같이 추가해주고 pub get을 눌러준다.
flutter:
uses-material-design: true
fonts:
- family: dancingscript
fonts:
- asset: asset/font/DancingScript-Regular.ttf
- asset: asset/font/DancingScript-Light.ttf
weight: 300
- asset: asset/font/DancingScript-Medium.ttf
weight: 500
- asset: asset/font/DancingScript-Bold.ttf
weight: 700
이런 식으로 family이름을 지정해주고 weight에 따라 어떤 글꼴을 쓸것인지도 정의 할 수 있다
Text(
'Home',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w700),
)
이런식으로 테마에서 정의한 bodyText1
을 가져다 쓸 수 있으며, copyWith
을 통해 추가로 스타일링을 해줄 수도 있다.
이 이외에도 debugShowCheckedModeBanner
을 사용하면 에뮬레이터 상단우측에 뜨는 debug표시를 지울수도 있다. 하지만 이 옵션을 킬 경우 에러가 날때 제대로된 에러를 뱉지 못하므로 개발중에는 키는것을 권장한다(캡쳐해서 자랑할때만 켜주자!)
정리해보자면 MaterialApp
은 앱의 루트 부분이며 공통된 부분의 설정을 하여 중복된 코드를 줄이고 수정하기 쉬운 코드를 작성하는데 도움이 된다. 지금까지 하드코딩 해왔다면 MaterialApp
을 사용하여 더 유지보수하기 쉬운 코드를 작성해보자
포스팅에 대한 코드
GitHub - ghdic/flutter_lib: flutter전용 레포
flutter전용 레포. Contribute to ghdic/flutter_lib development by creating an account on GitHub.
github.com
MaterialApp Docs
MaterialApp class - material library - Dart API
An application that uses Material Design. A convenience widget that wraps a number of widgets that are commonly required for Material Design applications. It builds upon a WidgetsApp by adding material-design specific functionality, such as AnimatedTheme a
api.flutter.dev