这个想法很简单。用户使用 facebook/google/instagram 登录并从上述服务中获取 accessToken。此令牌必须与需要身份验证的每个请求一起发送。
使用flutter storage保存token:https://pub.dev/packages/flutter_secure_storage
我将向您发布我的一个 Flutter 应用程序示例,其中我使用 instagram 实现了登录。
恢复下面的代码:
-
loginService.dart负责登录api调用并将token保存到存储中
-
main.dart 是应用程序的第一个视图。如果用户未登录,则会在此处显示 Login 小部件(LoginWidget 在 login.dart 中)。
-
login.dart 是登录页面的视图。有一个按钮从LoginService类调用函数
代码如下:
~/lib/service/loginService.dart
import 'dart:async';
import 'dart:convert' as JSON;
import 'package:http/http.dart' as http;
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'package:choose/model/User.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:choose/config.dart' as config;
String apiBaseUrl = config.apiBase;
String apiPort = config.port;
class LoginService {
String clientID;
String secretID;
String redirectURI;
final FlutterSecureStorage storage = new FlutterSecureStorage();
final String authKey = "token";
LoginService(this.clientID, this.secretID, this.redirectURI);
String get authenticationURI {
return "https://api.instagram.com/oauth/authorize/?client_id=$clientID&redirect_uri=$redirectURI&response_type=code";
}
String get authorizationURI {
return "https://api.instagram.com/oauth/access_token";
}
Future<User> authenticate() async {
clean();
FlutterWebviewPlugin _flutterWebviewPlugin = FlutterWebviewPlugin();
// Stream<String> onCode = await _openServer();
_flutterWebviewPlugin.launch(
authenticationURI
);
Completer<User> loginCompleter = Completer();
_flutterWebviewPlugin.onStateChanged.listen((viewState) async {
String url = viewState.url;
int indexOfCode = url.lastIndexOf("?code=");
if(url != null && indexOfCode >= 0 && viewState.type == WebViewState.finishLoad) {
String code = url.substring(url.indexOf('?code=') + 6, url.length);
http.Response userResponse = await http.get(
"${config.endpoints['getUserByCode']}?code=$code",
);
if(userResponse.statusCode == 200) {
User user = User.fromMap(JSON.jsonDecode(userResponse.body));
saveToken(user.token);
loginCompleter.complete(user);
} else {
loginCompleter.completeError("User not found");
}
_flutterWebviewPlugin.close();
}
});
return loginCompleter.future;
}
void saveToken(token) async {
storage.write(key: authKey, value: token);
}
void clean() async {
storage.delete(key: authKey);
}
Future<String> getToken() async {
return storage.read(key: authKey);
}
static Future<String> getStaticToken() async {
FlutterSecureStorage storage = FlutterSecureStorage();
return storage.read(key: "token");
}
}
~lib/main.dart
import 'package:choose/friends.dart';
import 'package:choose/model/User.dart';
import 'package:flutter/material.dart';
import 'package:choose/login.dart';
import 'package:choose/service/loginService.dart';
import 'package:web_socket_channel/io.dart';
import 'feed.dart';
import 'notification.dart';
import 'newPost.dart';
import 'config.dart' as config;
void main() => runApp(MyApp());
LoginService loginService = new LoginService(
"71b818abd18043fb8b7c1833912b62ae",
"3d9d6f87d2354085a74534c13bdda51f",
config.endpoints['authorize'],
);
class MyApp extends StatelessWidget {
// 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: MyHomePage(title: 'Flutter Demo Home Page'),
routes: {
'/feed': (context) => DefaultTabController(
length: 2,
child: FeedWidget()
),
'/newpost': (context) => NewPost(),
'/notifications': (context) => NotificationWidget(
channel: IOWebSocketChannel.connect(config.websocketURI)
),
'/friends': (context) => FriendsWidget(),
}
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
User user;
String label = 'Continue with Instagram';
void openLoginPage() async {
try {
User _user = await loginService.authenticate();
this.openPostPage();
} catch(e) {
this.setState(() {
label = 'Try Again';
});
}
}
void openPostPage() async {
print(await loginService.getToken());
print("token");
Navigator.pushReplacementNamed(context, '/feed');
}
void actionDelegator() {
if(user != null) {
return openPostPage();
} else {
return openLoginPage();
}
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Login(loginAction: actionDelegator, user: user, label: label);
}
}
~/lib/service/login.dart
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:choose/model/User.dart';
class Login extends StatefulWidget {
Login({this.loginAction, this.user, this.label});
final loginAction;
final User user;
final String label;
_Login createState() => _Login();
}
class _Login extends State<Login> {
String imageURI = "https://www.travelcontinuously.com/wp-content/uploads/2018/04/empty-avatar.png";
@override
Widget build(BuildContext context) {
if(widget.user != null && widget.user.profilePicture != '' ) {
imageURI = widget.user.profilePicture;
}
return Container (
color: Colors.green,
child: Column (
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar (
backgroundColor: Colors.transparent,
radius: 50.0,
backgroundImage: NetworkImage(imageURI, scale: 2.0),
),
Container(
padding: EdgeInsets.all(0.0),
margin: EdgeInsets.all(20.0),
color: Colors.transparent,
child: MaterialButton(
elevation: 5.0,
color: Color.fromRGBO(193, 53, 132, 1.0),
child: FlatButton.icon(
label: Text(widget.label, style: TextStyle(color: Color.fromRGBO(255,220,128, 1.0), fontWeight: FontWeight.w900, fontSize: 16.0)),
icon: Icon(FontAwesomeIcons.instagram, color: Color.fromRGBO(255,220,128, 1.0)),
),
onPressed: () async {
widget.loginAction();
},
)
)
],
),
);
}
}