일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- React JS
- MS-SQL
- 닷넷
- Animation
- 함수
- MVVM
- GitHub
- 마우이
- 바인딩
- Flutter
- AnimationController
- 깃허브
- 자바스크립트
- listview
- 리엑트
- db
- Firebase
- Maui
- 오류
- MSSQL
- 플러터
- spring boot
- 파이어베이스
- 애니메이션
- page
- Binding
- JavaScript
- typescript
- HTML
- .NET
- Today
- Total
개발노트
40. [Flutter] GoRouter 사용하기 (Navigator 2.0) 본문
GoRouter
GoRouter 방식(Navigator 2.0)이 필요한 이유
Flutter가 Web을 지원하면서 url 방식으로 Router를 설정하여 Navigation 할 수 있는 방식이 필요해졌습니다.
만약 Android와 IOS만 지원하는 App을 개발중이라면 앞서 포스팅한 Navigator 1.0방식을 사용하셔도됩니다.
- Navigator 1.0방식: https://mroh1226.tistory.com/71
하지만, Web 서비스까지 지원하는 App이라면 GoRouter라는 Navigation 2.0 방식을 채택해야합니다.
(Navigator 1.0 방식은 url을 지원하지 않기 때문에, Web의 앞으로가기 기능을 지원하지않음)
기능에 따라서는 Navigator 2.0은 Navigator 1.0과 함께 사용할 수도 있습니다.
(예를들면 화면은 변하지만 url은 변하지않는 화면, url을 통해서 특정 화면에 들어가는 것을 막을 때)
GoRouter 마이그레이션 시작 전, 준비물.
아래 링크로가서 GoRouter를 설치해주세요.(포스팅 내용은 v12.0.0 기준입니다.)
- Navigater 2.0방식 GoRouter pub.dev 설치 링크: https://pub.dev/packages/go_router
1. GoRouter 생성하기
- main.dart 에서 Material.router() 위젯의 routerConfig: 속성에 router 설정하기 위해 필요한 작업입니다.
예시.1) MainPage와 TestScreen이 각개 화면일 경우
final GoRouter router = GoRouter(routes: [
GoRoute(
path: "/",
builder: (context, state) => const MainPage(),
),
GoRoute(
path: "/test",
builder: (context, state) => const TestScreen(),
)
]);
- 이 경우에는 각각 처음으로 Stack에 들어가는 화면으로 "/"가 필요합니다.
예시.2) MainPage안에 TestScreen 이 Tree구조로 이뤄질 경우 (서브루트)
final GoRouter router = GoRouter(routes: [
GoRoute(
path: "/",
builder: (context, state) => const MainPage(),
routes: [
GoRoute(
path: "test",
builder: (context, state) => const TestScreen(),
)
]),
]);
- 이 경우에는 MainPage 다음으로 TestScreen이 Stack으로 쌓이기 때문에 서브루트(TestScreen)의 Path 앞에는 "/" 슬래시를 붙이지 않습니다.
2. main.dart에 router 설정하기
- main.dart 의 MaterialApp Widget을 MaterialApp.router() 로 변경합니다.
- routerConfig: 속성에 위에서 생성해준 router를 매핑해줍니다.
- home: 속성은 지워줍니다.
4. 각각 화면전환 Tap 메소드에 있는 Navigator를 context로 수정하기
[변경 전 코드]
void onTap(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const TestScreen(),
));
}
[변경 후]
void onTap(BuildContext context) {
context.pop();
context.push("/test");
}
*context.go() 사용으로 기존 화면 Stack 무시하고 새로운 화면 띄우기
context.go("/test");
3.(권장사항) Named Router 형태로 바꾸기
Named Router 방식과 위의 Path 방식 차이는 Route에 Name을 부여해서 서브루트 호출 시에 Url이 길어지는 것을 방지할 수 있습니다.
예를 들어
Named Router 방식 없이 APage 안에 BPage 안에있는 CPage 서브루트를 호출한다면,
context.push("/APage/BPage/CPage"); 와 같이 작성해야하지만,
Named Router를 사용한다면,GoRoute에서 GoRoute( name: "CPage"... 로 설정 한뒤, 아래와 같이 작성하면 끝입니다.
context.pushNamed("CPage");
[Named Router 작성법]
final GoRouter route = GoRouter(routes: [
GoRoute(
name: "main",
path: "/", //가장 처음 route의 path에는 맨앞에'/' 를 붙여줌
builder: (context, state) => const MainPage(),
routes: [
GoRoute(
name: "test",
path: "test",
builder: (context, state) => const TestScreen(),
)
]),
]);
서브루트든 그냥 루트든, GoRoute에서 name: "test" 로 name을 부여하고 호출 시에도 아래와 같이 name을 적어주면 됨
*가장 처음 route의 path에는 맨앞에'/' 를 붙여줌
context.pushNamed("test");
context.goNamed("test");
// Named Route 안쓰면 이렇게 써야함: context.push("/login/test");
4. Parameter로 화면 간에 값 넘기는 방법(3가지 방법)
4-1) pathParameter: 를 사용할 경우
- pathParameter를 사용할 경우 url에 전달한 값이 포함됩니다.(노출됨)
[Router 부분 소스]
GoRoute(
name: "test",
path: "test",
builder: (context, state) => const TestScreen(),
routes: [
GoRoute(
name: "test2",
path: "test2/:value",
builder: (context, state) {
final String value = state.pathParameters['value']!;
//final String value = state.uri.queryParameters['value']!;
return TestScreen2(
value: value,
);
},
)
]),
- Router 부분의 path: 속성에 "page/:value" 와 같이 전달할 값을 :/값 으로 작성합니다.
- final String value = state.pathParameters['value']!; 과 같이 pathParameter 값을 받아서
- TestScreen2(value: value); 에 화면 생성자 인자 값에 넣어줍니다.
[보낼 화면 소스]
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class TestScreen extends StatefulWidget {
static String routeName = 'test';
static String routeURL = 'test';
const TestScreen({super.key});
@override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final TextEditingController _controller = TextEditingController();
late String _text = "";
void onTap(BuildContext context) {
context.pushNamed('test2', pathParameters: {"value": _text});
}
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {
_text = _controller.text;
});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('다음 페이지에 전달할 값을 입력'),
SizedBox(
width: 300,
child: TextField(
controller: _controller,
)),
CupertinoButton(
onPressed: () => onTap(context), child: const Text('클릭'))
],
),
),
);
}
}
- TextEditingController 를 생성하여 입력된 실시간으로 값을 저장합니다.
- context.pushNamed('test2', pathParameters: {"value": _text}); 처럼 호출 방식을 작성하고 pathParameters: 에 Map<String,String> 형식에 맞춰 para이름: 값 으로 작성해줍니다.
[받는 화면 소스]
import 'package:flutter/material.dart';
class TestScreen2 extends StatelessWidget {
static String routeName = 'test2';
static String routeURL = 'test2';
final String value;
const TestScreen2({super.key, required this.value});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text("전달받은 값"),
Text(
value,
style: const TextStyle(fontSize: 32),
)
],
),
),
);
}
}
- 값을 전달 받기위해 value 처럼 생성자에 required this.value로 변수를 추가합니다.
- 전달받은 value 값을 사용합니다.
4-2) queryParameters:를 사용할 경우
- 보내야할 값이나 Type이 정해져있어 이를 선택한 값을 보낼 때 사용합니다.
[Router 부분 소스]
GoRoute(
name: "test",
path: "test",
builder: (context, state) => const TestScreen(),
routes: [
GoRoute(
name: "test2",
path: "test2/:value",
builder: (context, state) {
final String value = state.pathParameters['value']!;
final String type = state.uri.queryParameters['type']!;
return TestScreen2(value: value, type: type);
},
)
]),
- final String type = type state.uri.queryParameters['type']! 과 같이 사용할 para명을 추가합니다.
[값을 보내는 화면 소스]
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class TestScreen extends StatefulWidget {
static String routeName = 'test';
static String routeURL = 'test';
const TestScreen({super.key});
@override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final TextEditingController _controller = TextEditingController();
late String _text = "";
late bool _type = false;
void onTap(BuildContext context) {
context.pushNamed('test2',
pathParameters: {"value": _text},
queryParameters: {"type": _type ? "체크함" : "체크안함"});
}
void _onCheck() {
setState(() {
_type = !_type;
});
}
@override
void initState() {
super.initState();
_controller.addListener(() {
setState(() {
_text = _controller.text;
});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('다음 페이지에 전달할 값을 입력'),
SizedBox(
width: 300,
child: TextField(
controller: _controller,
)),
CupertinoButton(
onPressed: () => onTap(context), child: const Text('클릭')),
Checkbox(
value: _type,
onChanged: (value) => _onCheck(),
)
],
),
),
);
}
}
- 체크박스를 통해 기능을 추가하고 pushNamed 에 queryParameters를 추가해줍니다.
[값을 받는 화면 소스]
import 'package:flutter/material.dart';
class TestScreen2 extends StatelessWidget {
static String routeName = 'test2';
static String routeURL = 'test2';
final String value;
final String type;
const TestScreen2({super.key, required this.value, required this.type});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text("전달받은 값"),
Text(
value,
style: const TextStyle(fontSize: 32),
),
Text(type)
],
),
),
);
}
}
- 값을 전달 받기위해 value 처럼 생성자에 required this.type로 변수를 추가합니다.
- 전달받은 type 값을 사용합니다.
4-3) extra:를 사용할 경우(매우 간단함)
- url에 값이 포함되지 않아 전달되는 값을 보호하고 싶을 때 사용합니다.
- 이전 화면의 호출을 통해서만 다음화면이 호출되어야만 할 때 사용합니다.(Url로 접근 불가)
[값을 보낼 화면을 호출하는 매소드안에 작성]
context.pushNamed("NextScreen", extra: value); //extra: 보낼값
[받는 화면에서 받은 값을 사용하는 방법]
class NextScreen extends StatefulWidget {
final String value; //GoRoute 에서 작성한 extra: Data형
const EmailScreen({super.key, required this.value});
...
5.(선택사항) 화면 전환에 Animation 적용하기.
Builder: 대신에 PageBuilder: 를 사용하면 아래와 같이 Animation 효과를 줄 수 있습니다.
GoRoute(
name: "TestPage",
path: TestScreen.routeName,
pageBuilder: (context, state) {
return CustomTransitionPage(
child: const TestScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
},
);
},
),
- Animation에 관한 내용은 이전 포스팅 참고: https://mroh1226.tistory.com/134,https://mroh1226.tistory.com/135
'앱 개발 > Flutter' 카테고리의 다른 글
42. [Flutter] ImagePicker 앨범 이미지, 동영상 가져오기 및 촬영하기 (0) | 2023.11.07 |
---|---|
41. [Flutter] Camera 녹화하고 gallery_saver 로 영상 저장하기 (with Permission_handler 장치 (0) | 2023.11.02 |
39. [Flutter] Google Font 사용하기(TextStyle 통일하기 + Typography) (1) | 2023.10.18 |
38. [Flutter] 다크모드, 라이트모드 테마 커스텀 하기(with ThemeMode) (0) | 2023.10.18 |
37.[Flutter] LayoutBuilder 로 위젯 크기 가져오기 (vs MediaQuery) (2) | 2023.10.13 |