简体   繁体   English

在 Google Flutter 应用程序中管理社交用户详细信息

[英]Manage social user details in Google Flutter application

I am a beginner in developing Flutter application, and try to create a sample application for educational purpose.我是开发 Flutter 应用程序的初学者,并尝试创建一个用于教育目的的示例应用程序。 Last few weeks I decided to do a sample application in flutter that have no inbuilt login or register section because it's have social login options like Facebook and Google.上周我决定在 flutter 中做一个没有内置登录或注册部分的示例应用程序,因为它具有 Facebook 和 Google 等社交登录选项。 I searched the web and got many code examples for how to implement Facebook and Google authentication in Flutter application.我搜索了 web 并获得了许多关于如何在 Flutter 应用程序中实现 Facebook 和 Google 身份验证的代码示例。

I have a doubt about this social login implementation maybe it's happening because of lack of working experience in mobile application architecture level.我对这种社交登录实现存在疑问,可能是因为缺乏移动应用程序架构级别的工作经验。 At this point I am asking a question to myself "How to manage social login users in applications"此时我在问自己一个问题“如何在应用程序中管理社交登录用户”

I have a solution like if the user can login ed successfully, store the user details into our (Firebase) db check the user email exists in database if there is no matching entries in db it will create a new user in database or if exists update the last login date time of that particular user.我有一个解决方案,比如如果用户可以成功登录,将用户详细信息存储到我们的(Firebase)数据库中,检查用户 email 是否存在于数据库中,如果数据库中没有匹配的条目,它将在数据库中创建一个新用户,或者如果存在更新该特定用户的上次登录日期时间。 Because this application shows some user related data in other forms, it's work on the basis of logined userid, so I need to store the logined user details in the database.因为这个应用程序显示了其他 forms 中的一些用户相关数据,它是在登录用户 ID 的基础上工作的,所以我需要将登录用户详细信息存储在数据库中。

Your proposed solution is good enough.您提出的解决方案已经足够好了。 The returned id is unique for your app.返回的 id 对于您的应用程序是唯一的。

Use the id returned to your app as the identifier for users in your app.使用返回给您的应用的 id 作为您应用中用户的标识符。

Checkout this answer on unique id在唯一 ID 上查看此答案

Just leaving the social_login plugin here只需将social_login插件留在这里

// Import package import 'package:social_login/social_login.dart'; // Instantiate it final socialLogin = SocialLogin(); //Before calling any methods, set the configuration socialLogin.setConfig(SocialConfig( facebookAppId: FACEBOOK_APP_ID, googleWebClientId: GOOGLE_WEB_CLIENT_ID, /*In case a Google tokenId is needed*/ twitterConsumer: TWITTER_CONSUMER_KEY, twitterSecret: TWITTER_CONSUMER_SECRET, )); // Get current logged user final FacebookUser facebookUser = await socialLogin.getCurrentFacebookUser(); final GoogleUser googleUser = await socialLogin.getCurrentGoogleUser(); final TwitterUser twitterUser = await socialLogin.getCurrentTwitterUser(); //Log in social networks final FacebookUser facebookUser = await socialLogin.logInFacebookWithPermissions(FacebookPermissions.DEFAULT); final GoogleUser googleUser = await socialLogin.logInGoogle(); final TwitterUser twitterUser = await socialLogin.logInTwitter(); //Log out from social networks await socialLogin.logOutFacebook(); await socialLogin.logOutGoogle(); await socialLogin.logOutTwitter();

By using the built-in sign in using Firebase Authentication SDK ( Official Guide ), you will be able to obtain login information for different platforms like Google, Twitter, Facebook and even Github. By using the built-in sign in using Firebase Authentication SDK ( Official Guide ), you will be able to obtain login information for different platforms like Google, Twitter, Facebook and even Github.

You do not need to code your own login/signup, everything is handle hassle free by using the right function from Firebase.您无需编写自己的登录/注册代码,使用来自 Firebase 的正确 function 即可轻松处理一切。

After that you will have the login information like so (this is from email login but Facebook, Google, Twitter and Github is available):之后,您将获得像这样的登录信息(这是来自 email 登录,但 Facebook、Google、Twitter 和 ZE1ADBC6192C62730 可用)

在此处输入图像描述

You can then link either the identifier or the User UID to the information in your database.然后,您可以将标识符或用户 UID 链接到数据库中的信息。

在此处输入图像描述

Oh and last but not least, do remember to set the persistence setting for the login provider to keep user login after they close their apps.哦,最后但同样重要的是,请记住为登录提供程序设置持久性设置,以在用户关闭应用程序后保持用户登录。

You should split Flutter app into 2 parts:您应该将 Flutter 应用程序分成两部分:

  1. The "main" part of the app.应用程序的“主要”部分。 It shows user related data.它显示用户相关数据。
  2. Sign in/Sign up part.登录/注册部分。

The logic is quite simple.逻辑很简单。 On the app start you check if the user is authenticated.在应用程序启动时,您检查用户是否已通过身份验证。 If yes, navigate him to the main screen.如果是,请将他导航到主屏幕。 If not, present him a sign up screen.如果没有,请给他一个注册屏幕。 Here's how it can be achieved using Firebase as a backend:以下是使用 Firebase 作为后端的方法:

final currentUser = await FirebaseAuth.instance.currentUser();
if (currentUser != null){
  // We're good: the user is authenticated.
  // Show him the main screen.
  Navigator.of(context).pushReplacement(MaterialPageRoute(
    builder: (context) => HomeScreen()
  ));
} else {
  // The user is not logged in.
  // Show him the sign in/sign up screen.
  Navigator.of(context).pushReplacement(MaterialPageRoute(
    builder: (context) => SignInScreen()
  ));
}

Maybe this example meet your needs:也许这个例子可以满足您的需求:

https://github.com/instaflutter/flutter-login-screen-firebase-auth-facebook-login https://github.com/instaflutter/flutter-login-screen-firebase-auth-facebook-login

If you need some help to understand, i can help you!如果您需要一些帮助来理解,我可以帮助您!

you should go with the token logic (which is used by google and facebook).你应该 go 与令牌逻辑(由谷歌和脸书使用)。

you should generate a token to each user and store it in your data base.您应该为每个用户生成一个令牌并将其存储在您的数据库中。 through this token you can mange every thing about the user.通过这个令牌,你可以管理关于用户的每一件事。 when you call facebook authtinicating it will give you back a token for specific user.当您致电 facebook 进行身份验证时,它将为您返回特定用户的令牌。 this token when you send it again to facebook or google they will respond back with the details of the user.当您再次将此令牌发送到 facebook 或谷歌时,他们将回复用户的详细信息。 so you can work like them.. create a token for the user and recall it for the info from your data base.. and you can store the token responded from facebook and match it in the future.. the purpose of my answer to go and search for token concept cause it will give you a lot of benefits.所以你可以像他们一样工作..为用户创建一个令牌并从你的数据库中调用它的信息..你可以存储从 facebook 响应的令牌并在将来匹配它..我对 go 的回答的目的并搜索令牌概念,因为它会给您带来很多好处。

The idea is simple.这个想法很简单。 The users get logged in with facebook/google/instagram and get an accessToken from the services mentioned above.用户使用 facebook/google/instagram 登录并从上述服务中获取 accessToken。 This token has to be sent with each request that need authentication.此令牌必须与需要身份验证的每个请求一起发送。

Use flutter storage to save the persist the token: https://pub.dev/packages/flutter_secure_storage使用 flutter 存储来保存持久化令牌: https://pub.dev/packages/flutter_secure_storage

I will post you an example of one of my flutter app where I implemented the login with instagram.我将向您发布我的 flutter 应用程序之一的示例,其中我使用 instagram 实现了登录。

Resuming the code below:恢复下面的代码:

  • loginService.dart is responsible for login api calls and saving the token into the storage loginService.dart负责登录 api 调用并将令牌保存到存储中
  • main.dart is the first view of the app. main.dart是应用程序的第一个视图。 Here if the user is not logged in the Login widget is shown ( the LoginWidget is in login.dart ).如果用户未登录,则会显示 Login 小部件( LoginWidget位于 login.dart 中)。
  • login.dart is the view of the login page. login.dart是登录页面的视图。 There is a button that calls functions from LoginService class有一个按钮从LoginService class 调用函数

Here's the code:这是代码:

~/lib/service/loginService.dart ~/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 ~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 ~/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();
              },
            )
          )
        ],
      ),
    );
  }

}

(Someone might find this helpful). (有人可能会觉得这很有帮助)。 For easy oauth in flutter, Try visa - https://github.com/e-oj/visa为了在 flutter 中轻松 oauth,请尝试签证 - https://github.com/e-oj/visa

Here's an example with Facebook auth (It also supports google, twitch, discord, and Github):这是 Facebook auth 的示例(它还支持 google、twitch、discord 和 Github):

import 'package:visa/auth-data.dart';
import 'package:visa/fb.dart';

class AuthPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /// Simply Provide all the necessary credentials
      body: FaceBookAuth().visa.authenticate(
          clientID: '139732240983759',
          redirectUri: 'https://www.e-oj.com/oauth',
          scope: 'public_profile,email',
          state: 'fbAuth',
          onDone: done
      )
    );
  }
}

and the "done" callback:和“完成”回调:

done(AuthData authData){
  print(authData);

  /// You can pass the [AuthData] object to a 
  /// post-authentication screen. It contaions 
  /// all the user and OAuth data collected during
  /// the authentication process. In this example,
  /// our post-authentication screen is "complete-profile".
  Navigator.pushReplacementNamed(
      context, '/complete-profile', arguments: authData
  );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM