0

我是新来的。

我正在使用拦截器来捕获 API 可能响应的 401 http 代码。但我真的不明白如何让它在它发生时进入登录页面。我想使用 Navigator,但它需要我没有的上下文。

class AuthInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    print(data);
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    if (data.statusCode == 401) {
      // What to do to go to login page?
    }
    return data;
  }
}

有人可以帮我吗?

4

1 回答 1

1

您可以复制粘贴下面运行完整代码
我使用官方天气示例来模拟这种情况,因为OPEN_WEATHER_API_KEY无效将返回401
您可以将上下文传递给AuthInterceptor初始化时

代码片段

@override
  void initState() {
    repository = WeatherRepository(
      HttpClientWithInterceptor.build(interceptors: [
        AuthInterceptor(context: context),
      ]),
    );
...
class AuthInterceptor implements InterceptorContract {
  final BuildContext context;
  AuthInterceptor({this.context});  

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    print("status code ${data.statusCode}");
    if (data.statusCode == 401) {
      Future.delayed(Duration(seconds: 1), () {
        Navigator.pushNamedAndRemoveUntil(
            context, "/", (Route<dynamic> route) => false);
      });
    }
    return data;
  }
}

工作演示

在此处输入图像描述

完整代码

import 'dart:convert';
import 'dart:io';

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

const String OPEN_WEATHER_API_KEY = "YOUR-KEY-HERE";

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => Login(),
        '/home': (context) => HomeScreen(),
      },
    );
  }
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  WeatherRepository repository;

  @override
  void initState() {
    repository = WeatherRepository(
      HttpClientWithInterceptor.build(interceptors: [
        AuthInterceptor(context: context),
      ]),
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('Weather App'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.search),
            onPressed: () {
              showSearch(
                context: context,
                delegate: WeatherSearch(repository),
              );
            },
          )
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Icon(
              Icons.wb_sunny,
              size: 64,
              color: Colors.grey,
            ),
            Container(
              height: 16,
            ),
            Text(
              "Search for a city",
              style: TextStyle(
                fontSize: 24.0,
                fontWeight: FontWeight.w300,
              ),
              textAlign: TextAlign.center,
            ),
          ],
        ),
      ),
    );
  }
}

class WeatherSearch extends SearchDelegate<String> {
  int selected = -1;
  WeatherRepository repo;

  WeatherSearch(this.repo);

  @override
  List<Widget> buildActions(BuildContext context) {
    return [
      IconButton(
        icon: Icon(Icons.clear),
        onPressed: () {
          selected = -1;
          query = "";
        },
      )
    ];
  }

  @override
  Widget buildLeading(BuildContext context) {
    return IconButton(
      icon: AnimatedIcon(
        icon: AnimatedIcons.menu_arrow,
        progress: transitionAnimation,
      ),
      onPressed: () {
        close(context, null);
      },
    );
  }

  @override
  Widget buildResults(BuildContext context) {
    final city = selected == -1 ? null : cities[selected];

    return city != null ? buildWeatherCard(city) : buildEmptyCard();
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    final suggestionList = query.isEmpty
        ? cities
        : cities.where((p) => p["name"].toString().startsWith(query)).toList();
    return ListView.builder(
      itemCount: suggestionList.length,
      itemBuilder: (context, index) {
        return ListTile(
          onTap: () {
            selected = index;
            query = cities[selected]["name"];
            showResults(context);
          },
          title: Text(suggestionList[index]['name']),
          subtitle: Text(suggestionList[index]['country']),
        );
      },
    );
  }

  Widget buildWeatherCard(final city) {
    return FutureBuilder(
      future: repo.fetchCityWeather(city["id"]),
      builder: (context, snapshot) {
        if (snapshot.hasError) {
          return Center(
            child: Text(snapshot.error),
          );
        }

        if (!snapshot.hasData) {
          return Center(
            child: CircularProgressIndicator(),
          );
        }
        final weather = snapshot.data;
        final iconWeather = weather["weather"][0]["icon"];
        final main = weather["main"];
        final wind = weather["wind"];
        return Card(
          margin: EdgeInsets.all(16.0),
          child: Container(
            width: Size.infinite.width,
            padding: EdgeInsets.all(16.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                ListTile(
                  leading: Tooltip(
                    child: Image.network(
                        "https://openweathermap.org/img/w/$iconWeather.png"),
                    message: weather["weather"][0]["main"],
                  ),
                  title: Text(city["name"]),
                  subtitle: Text(city["country"]),
                ),
                ListTile(
                  title: Text("${main["temp"]} °C"),
                  subtitle: Text("Temperature"),
                ),
                ListTile(
                  title: Text("${main["temp_min"]} °C"),
                  subtitle: Text("Min Temperature"),
                ),
                ListTile(
                  title: Text("${main["temp_max"]} °C"),
                  subtitle: Text("Max Temperature"),
                ),
                ListTile(
                  title: Text("${main["humidity"]} %"),
                  subtitle: Text("Humidity"),
                ),
                ListTile(
                  title: Text("${main["pressure"]} hpa"),
                  subtitle: Text("Pressure"),
                ),
                ListTile(
                  title: Text("${wind["speed"]} m/s"),
                  subtitle: Text("Wind Speed"),
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  Widget buildEmptyCard() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Icon(
            Icons.wb_sunny,
            size: 64,
            color: Colors.grey,
          ),
          Container(
            height: 16,
          ),
          Text(
            "Search for a city",
            style: TextStyle(
              fontSize: 24.0,
              fontWeight: FontWeight.w300,
            ),
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }
}

const baseUrl = "https://api.openweathermap.org/data/2.5";

class WeatherRepository {
  HttpClientWithInterceptor client;

  WeatherRepository(this.client);

  // Alternatively you can forget about using the Client and just doing the HTTP request with
  // the HttpWithInterceptor.build() call.
  // Future<Map<String, dynamic>> fetchCityWeather(int id) async {
  //   var parsedWeather;
  //   try {
  //     var response = await HttpWithInterceptor.build(
  //             interceptors: [WeatherApiInterceptor()])
  //         .get("$baseUrl/weather", params: {'id': "$id"});
  //     if (response.statusCode == 200) {
  //       parsedWeather = json.decode(response.body);
  //     } else {
  //       throw Exception("Error while fetching. \n ${response.body}");
  //     }
  //   } catch (e) {
  //     print(e);
  //   }
  //   return parsedWeather;
  // }

  Future<Map<String, dynamic>> fetchCityWeather(int id) async {
    var parsedWeather;
    try {
      final response =
          await client.get("$baseUrl/weather", params: {'id': "$id"});
      if (response.statusCode == 200) {
        parsedWeather = json.decode(response.body);
      } else {
        return Future.error(
          "Error while fetching.",
          StackTrace.fromString("${response.body}"),
        );
      }
    } on SocketException {
      return Future.error('No Internet connection ');
    } on FormatException {
      return Future.error('Bad response format ');
    } on Exception {
      return Future.error('Unexpected error ');
    }

    return parsedWeather;
  }
}

class AuthInterceptor implements InterceptorContract {
  final BuildContext context;
  AuthInterceptor({this.context});

  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    try {
      data.params['appid'] = OPEN_WEATHER_API_KEY;
      data.params['units'] = 'metric';
      data.headers[HttpHeaders.contentTypeHeader] = "application/json";
    } catch (e) {
      print(e);
    }
    print(data.params);
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    print("status code ${data.statusCode}");
    if (data.statusCode == 401) {
      Future.delayed(Duration(seconds: 1), () {
        Navigator.pushNamedAndRemoveUntil(
            context, "/", (Route<dynamic> route) => false);
      });
    }
    return data;
  }
}

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('Login Page')),
        body: Center(
            child: Column(
          children: [
            Text("Login Page"),
            RaisedButton(
              child: Text('Login Success'),
              onPressed: () {
                Navigator.pushReplacementNamed(context, '/home');
              },
            ),
          ],
        )));
  }
}

const cities = [
  {
    "id": 707860,
    "name": "Hurzuf",
    "country": "UA",
    "coord": {"lon": 34.283333, "lat": 44.549999}
  },
  {
    "id": 519188,
    "name": "Novinki",
    "country": "RU",
    "coord": {"lon": 37.666668, "lat": 55.683334}
  },
  {
    "id": 1283378,
    "name": "Gorkhā",
    "country": "NP",
    "coord": {"lon": 84.633331, "lat": 28}
  },
];
于 2020-11-23T02:06:55.050 回答