개발 |/Flutter

(작성중) BottomTabNav + GoRouter + provider

어섬 2023. 2. 11. 22:48

특정 변화가 있을 때 감지해서 자동으로 라우트를 바꿔주는 기능. 

Redirect와 Refresh 기능. 

 

특정 URL을 이용할 때, 그 쪽으로 이동시키는 방법. 

(나중에 웹 링크로 들어왔을 때, 바로 그 스크린으로 가게도 도와줄 것 같음) 

 

UserModel 로 로그인 확인하기

 

riverpod 사용

 

routerProver 인데 GoRouter 클래스를 리턴해야함. 

신기하넹.

그냥 GoRouter 통째로 넣어줌 Routes모두 포함된 버전으로

그러고 Widget에 provider그대로 가져감. 

 

고라우터에서 changeNotifier 혹은 Stream을 이용해서 라우트를 바꿔야함. 프로바이더니까

redirect 로직과 refresh로직을 그래서 Notifier에 새로 만들었음.

 

UserModel 

logout 메소드는 => state =null 만들어주고

login 메소드는 => UserModel(name: name) 으로 만들어준다. 

 

userProvider를 그래서 

Notifier에는 ref가 없지, 

Notifier에서 생성자쓰고 required로 Notifier에 ref받아서 사용해도 된다. 나쁘지 않음.

많이 쓰는 패턴이라고 함. 

그러고 생성자 뒤에 {} 써줘서 나처럼 생성 될때 바로 작동하는 코드 넣어줬음. 거기에
ref.listen(provider)해줌. 

 

notifyListeners(); 를 실행하면 바뀌었다는 것을 알려주는 역할이래. 

String? _redirectLogit(GoRouterState state) 

현재 고라우터 상태를 주는게 GoRouterState ?라고 하는데요.. 

 

import 'package:flutter/src/widgets/placeholder.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

class ErrorScreen extends StatelessWidget {
  String error;
  ErrorScreen({super.key, required this.error});

  @override
  Widget build(BuildContext context) {
    return Center(child: Text(error));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("home"),
        ),
        body: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              ElevatedButton(
                  onPressed: () {
                    context.go('/one');
                  },
                  child: Text("go One screen")),
              ElevatedButton(
                  onPressed: () {
                    context.go('/one/two');
                  },
                  child: Text("go Two screen")),
              ElevatedButton(
                  onPressed: () {
                    context.goNamed(ThreeScreen.routeName);
                  },
                  child: Text("go Three screen")),
              ElevatedButton(
                  onPressed: () {
                    context.go('/login');
                  },
                  child: Text("go Login screen")),
            ]));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("one"),
        ),
        body: Center(child: Text("OneScreen")));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("two"),
        ),
        body: Center(child: Text("TwoScreen")));
  }
}

class ThreeScreen extends StatelessWidget {
  const ThreeScreen({super.key});
  static String get routeName => 'three';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("three"),
        ),
        body: Center(child: Text("ThreeScreen")));
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("login"),
        ),
        body: Center(
          child: ElevatedButton(
              onPressed: () {
                context.go('/');
              },
              child: Text("go home")),
        ));
  }
}

/******************************* */

class UserModel {
  String name;
  UserModel({required this.name});
}

/******************************* */

final routerProvider = Provider<GoRouter>(
  (ref) {
    final authStateProvider = AuthNotifier(ref: ref);

    return GoRouter(
        initialLocation: '/',
        errorBuilder: (context, state) {
          return ErrorScreen(error: state.error.toString());
        },
        redirect: authStateProvider._redirectLogic,
        refreshListenable: authStateProvider,
        routes: authStateProvider._routes);
  },
);

class AuthNotifier extends ChangeNotifier {
  final Ref ref;
  AuthNotifier({required this.ref}) {
    ref.listen(userProvider, (previous, next) {
      if (previous != next) {
        notifyListeners();
      }
    });
  }

  String? _redirectLogic(GoRouterState state) {
    // UserModel의 인스턴스 또는 null
    final user = ref.read(userProvider);

    //로그인을 하려는 상태인지 확인 : t
    final loggingIn = state.location == '/login';

    //유저 정보가 없다 - 로그인한 상태가 아니다
    //
    // 유저 정보가 없고
    // 로그인하는 중이 아니라면
    // 로그인 페이지로 이동한다.
    if (user == null) {
      return loggingIn ? null : '/login';
    }
    if (loggingIn) {
      return '/';
    }
  }

  List<GoRoute> get _routes => [
        GoRoute(path: '/', builder: (_, state) => HomeScreen2(), routes: [
          GoRoute(path: 'one', builder: (_, state) => OneScreen(), routes: [
            GoRoute(path: 'two', builder: (_, state) => TwoScreen(), routes: [
              GoRoute(
                name: ThreeScreen.routeName,
                path: 'three',
                builder: (_, state) => ThreeScreen(),
              )
            ])
          ])
        ]),
        GoRoute(path: '/login', builder: (_, state) => LoginScreen2())
      ];
}

final userProvider = StateNotifierProvider<UserStateNotifier, UserModel?>(
    (ref) => UserStateNotifier());

class UserStateNotifier extends StateNotifier<UserModel?> {
  UserStateNotifier() : super(null);

  login({required String name}) {
    state = UserModel(name: name);
  }

  logout() {
    state = null;
  }
}

 

'개발 | > Flutter' 카테고리의 다른 글

빡공 이틀차, flutter  (0) 2022.12.29
Navigator data 전달, (context).push , pop 메소드들  (0) 2022.12.27
GoRouter Redirect refresh  (0) 2022.12.11
[Flutter] 카카오 로그인  (0) 2022.11.12
[flutter] firebase 연동도 해본다  (0) 2022.11.06