Working Sidebar added

Unauthorized error fix
Settings Page (initial)
Redesinged PlaylistView with Table
This commit is contained in:
Kingkor Roy Tirtho 2022-01-04 22:45:26 +06:00
parent 809731f441
commit 76d0538f96
12 changed files with 399 additions and 220 deletions

BIN
assets/spotube-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -1,15 +1,26 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart' hide Page; import 'package:flutter/material.dart' hide Page;
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart' hide Image;
import 'package:spotube/components/CategoryCard.dart'; import 'package:spotube/components/CategoryCard.dart';
import 'package:spotube/components/Login.dart'; import 'package:spotube/components/Login.dart';
import 'package:spotube/components/Player.dart' as player; import 'package:spotube/components/Player.dart' as player;
import 'package:spotube/components/Settings.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/models/sideBarTiles.dart'; import 'package:spotube/models/sideBarTiles.dart';
import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/Auth.dart';
import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyDI.dart';
List<String> spotifyScopes = [
"user-library-read",
"user-library-modify",
"user-read-private",
"user-read-email",
"playlist-read-collaborative"
];
class Home extends StatefulWidget { class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key); const Home({Key? key}) : super(key: key);
@ -21,6 +32,8 @@ class _HomeState extends State<Home> {
final PagingController<int, Category> _pagingController = final PagingController<int, Category> _pagingController =
PagingController(firstPageKey: 0); PagingController(firstPageKey: 0);
int _selectedIndex = 0;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -28,24 +41,36 @@ class _HomeState extends State<Home> {
try { try {
Auth authProvider = context.read<Auth>(); Auth authProvider = context.read<Auth>();
SharedPreferences localStorage = await SharedPreferences.getInstance(); SharedPreferences localStorage = await SharedPreferences.getInstance();
String? clientId = localStorage.getString('client_id'); var clientId = localStorage.getString(LocalStorageKeys.clientId);
String? clientSecret = localStorage.getString('client_secret'); var clientSecret =
localStorage.getString(LocalStorageKeys.clientSecret);
var accessToken = localStorage.getString(LocalStorageKeys.accessToken);
var refreshToken =
localStorage.getString(LocalStorageKeys.refreshToken);
var expirationStr = localStorage.getString(LocalStorageKeys.expiration);
var expiration =
expirationStr != null ? DateTime.parse(expirationStr) : null;
if (clientId != null && clientSecret != null) { if (clientId != null && clientSecret != null) {
SpotifyApi spotifyApi = SpotifyApi( SpotifyApi spotifyApi = SpotifyApi(
SpotifyApiCredentials(clientId, clientSecret, scopes: [ SpotifyApiCredentials(
"user-library-read", clientId,
"user-library-modify", clientSecret,
"user-read-private", accessToken: accessToken,
"user-read-email", refreshToken: refreshToken,
"playlist-read-collaborative" expiration: expiration,
]), scopes: spotifyScopes,
),
); );
SpotifyApiCredentials credentials = await spotifyApi.getCredentials(); SpotifyApiCredentials credentials = await spotifyApi.getCredentials();
if (credentials.accessToken?.isNotEmpty ?? false) { if (credentials.accessToken?.isNotEmpty ?? false) {
authProvider.setAuthState( authProvider.setAuthState(
clientId: credentials.clientId, clientId: clientId,
clientSecret: credentials.clientSecret, clientSecret: clientSecret,
accessToken:
credentials.accessToken, // accessToken can be new/refreshed
refreshToken: refreshToken,
expiration: credentials.expiration,
isLoggedIn: true, isLoggedIn: true,
); );
} }
@ -89,75 +114,85 @@ class _HomeState extends State<Home> {
return Scaffold( return Scaffold(
body: Column( body: Column(
children: [ children: [
// Side Tab Bar
Expanded( Expanded(
child: Row( child: Row(
children: [ children: [
Container( NavigationRail(
color: Colors.blueGrey[50], backgroundColor: Colors.blueGrey[50],
constraints: const BoxConstraints(maxWidth: 230), destinations: sidebarTileList
child: Material( .map((e) => NavigationRailDestination(
type: MaterialType.transparency, icon: Icon(e.icon),
child: Column( label: Text(
children: [ e.title,
Flexible( style: const TextStyle(
flex: 1, fontWeight: FontWeight.bold,
// TabButtons fontSize: 16,
child: Column(
children: [
ListTile(
title: Text("Spotube",
style:
Theme.of(context).textTheme.headline4),
leading:
const Icon(Icons.miscellaneous_services),
), ),
const SizedBox(height: 20),
...sidebarTileList
.map(
(sidebarTile) => ListTile(
title: Text(sidebarTile.title),
leading: Icon(sidebarTile.icon),
onTap: () {},
), ),
) ))
.toList(), .toList(),
], selectedIndex: _selectedIndex,
onDestinationSelected: (value) => setState(() {
_selectedIndex = value;
}),
extended: true,
leading: Padding(
padding: const EdgeInsets.only(left: 15),
child: Row(children: [
Image.asset(
"assets/spotube-logo.png",
height: 50,
width: 50,
), ),
const SizedBox(
width: 10,
), ),
// user name & settings Text("Spotube",
style: Theme.of(context).textTheme.headline4),
]),
),
trailing:
Consumer<SpotifyDI>(builder: (context, data, widget) { Consumer<SpotifyDI>(builder: (context, data, widget) {
return FutureBuilder<User>( return FutureBuilder<User>(
future: data.spotifyApi.me.get(), future: data.spotifyApi.me.get(),
builder: (context, snapshot) { builder: (context, snapshot) {
var avatarImg = snapshot.data?.images?.last.url;
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment: MainAxisAlignment.spaceBetween,
MainAxisAlignment.spaceBetween,
children: [ children: [
if (avatarImg != null)
CircleAvatar(
child: CachedNetworkImage(
imageUrl: avatarImg,
),
),
Text( Text(
snapshot.data?.displayName ?? snapshot.data?.displayName ?? "User's name",
"User's name",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
IconButton( IconButton(
icon: icon: const Icon(Icons.settings_outlined),
const Icon(Icons.settings_outlined), onPressed: () {
onPressed: () {}), Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) {
return const Settings();
},
));
}),
], ],
), ),
); );
}, },
); );
}) }),
],
),
),
), ),
// contents of the spotify // contents of the spotify
if (_selectedIndex == 0)
Expanded( Expanded(
child: Scrollbar( child: Scrollbar(
child: PagedListView( child: PagedListView(
@ -170,6 +205,7 @@ class _HomeState extends State<Home> {
), ),
), ),
), ),
// player itself
], ],
), ),
), ),

View File

@ -2,8 +2,10 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart' hide Image;
import 'package:spotube/components/Home.dart';
import 'package:spotube/helpers/server_ipc.dart'; import 'package:spotube/helpers/server_ipc.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/Auth.dart';
class Login extends StatefulWidget { class Login extends StatefulWidget {
@ -12,38 +14,65 @@ class Login extends StatefulWidget {
} }
class _LoginState extends State<Login> { class _LoginState extends State<Login> {
String client_id = ""; String clientId = "";
String client_secret = ""; String clientSecret = "";
bool _fieldError = false; bool _fieldError = false;
String? accessToken;
String? refreshToken;
DateTime? expiration;
handleLogin(Auth authState) async { handleLogin(Auth authState) async {
try { try {
if (client_id == "" || client_secret == "") { if (clientId == "" || clientSecret == "") {
return setState(() { return setState(() {
_fieldError = true; _fieldError = true;
}); });
} }
final credentials = SpotifyApiCredentials(client_id, client_secret); final credentials = SpotifyApiCredentials(clientId, clientSecret);
final grant = SpotifyApi.authorizationCodeGrant(credentials); final grant = SpotifyApi.authorizationCodeGrant(credentials);
final redirectUri = "http://localhost:4304/auth/spotify/callback"; const redirectUri = "http://localhost:4304/auth/spotify/callback";
final scopes = ["user-library-read", "user-library-modify"];
final authUri = final authUri = grant.getAuthorizationUrl(Uri.parse(redirectUri),
grant.getAuthorizationUrl(Uri.parse(redirectUri), scopes: scopes); scopes: spotifyScopes);
final responseUri = await connectIpc(authUri.toString(), redirectUri); final responseUri = await connectIpc(authUri.toString(), redirectUri);
SharedPreferences localStorage = await SharedPreferences.getInstance();
if (responseUri != null) { if (responseUri != null) {
final SpotifyApi spotify = final SpotifyApi spotify =
SpotifyApi.fromAuthCodeGrant(grant, responseUri); SpotifyApi.fromAuthCodeGrant(grant, responseUri);
var credentials = await spotify.getCredentials();
if (credentials.accessToken != null) {
accessToken = credentials.accessToken;
await localStorage.setString(
LocalStorageKeys.accessToken, credentials.accessToken!);
}
if (credentials.refreshToken != null) {
refreshToken = credentials.refreshToken;
await localStorage.setString(
LocalStorageKeys.refreshToken, credentials.refreshToken!);
}
if (credentials.expiration != null) {
expiration = credentials.expiration;
await localStorage.setString(LocalStorageKeys.expiration,
credentials.expiration?.toString() ?? "");
}
} }
SharedPreferences localStorage = await SharedPreferences.getInstance(); await localStorage.setString(LocalStorageKeys.clientId, clientId);
await localStorage.setString('client_id', client_id); await localStorage.setString(
await localStorage.setString('client_secret', client_secret); LocalStorageKeys.clientSecret,
clientSecret,
);
authState.setAuthState( authState.setAuthState(
clientId: client_id, clientSecret: client_secret, isLoggedIn: true); clientId: clientId,
clientSecret: clientSecret,
accessToken: accessToken,
refreshToken: refreshToken,
expiration: expiration,
isLoggedIn: true,
);
} catch (e) { } catch (e) {
print(e); print("[Login.handleLogin] $e");
} }
} }
@ -57,6 +86,11 @@ class _LoginState extends State<Login> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.asset(
"assets/spotube-logo.png",
width: 400,
height: 400,
),
Text("Add your spotify credentials to get started", Text("Add your spotify credentials to get started",
style: Theme.of(context).textTheme.headline4), style: Theme.of(context).textTheme.headline4),
const Text( const Text(
@ -77,7 +111,7 @@ class _LoginState extends State<Login> {
), ),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
client_id = value; clientId = value;
}); });
}, },
), ),
@ -91,7 +125,7 @@ class _LoginState extends State<Login> {
), ),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
client_secret = value; clientSecret = value;
}); });
}, },
), ),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mpv_dart/mpv_dart.dart'; import 'package:mpv_dart/mpv_dart.dart';
import 'package:spotube/helpers/zero-pad-num-str.dart';
class PlayerControls extends StatefulWidget { class PlayerControls extends StatefulWidget {
final MPVPlayer player; final MPVPlayer player;
@ -43,10 +44,6 @@ class _PlayerControlsState extends State<PlayerControls> {
super.dispose(); super.dispose();
} }
String zeroPadNumStr(int input) {
return input < 10 ? "0$input" : input.toString();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var totalDuration = Duration(seconds: widget.duration.toInt()); var totalDuration = Duration(seconds: widget.duration.toInt());

View File

@ -1,8 +1,11 @@
import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:spotube/helpers/zero-pad-num-str.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/TrackButton.dart';
import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyDI.dart';
class PlaylistView extends StatefulWidget { class PlaylistView extends StatefulWidget {
@ -13,6 +16,70 @@ class PlaylistView extends StatefulWidget {
} }
class _PlaylistViewState extends State<PlaylistView> { class _PlaylistViewState extends State<PlaylistView> {
List<TableRow> trackToTableRow(List<Track> tracks) {
return tracks.asMap().entries.map((track) {
var thumbnailUrl = track.value.album?.images?.last.url;
var duration =
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
return (TableRow(
children: [
TableCell(
child: Text(
track.key.toString(),
textAlign: TextAlign.center,
)),
TableCell(
child: Row(
children: [
if (thumbnailUrl != null)
CachedNetworkImage(
imageUrl: thumbnailUrl,
maxHeightDiskCache: 40,
maxWidthDiskCache: 40,
),
const SizedBox(width: 10),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
track.value.name ?? "",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
),
overflow: TextOverflow.ellipsis,
),
Text(
(track.value.artists ?? [])
.map((e) => e.name)
.join(", "),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
TableCell(
child: Text(
track.value.album?.name ?? "",
overflow: TextOverflow.ellipsis,
),
),
TableCell(
child: Text(
duration,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
)
],
));
}).toList();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Consumer<SpotifyDI>(builder: (_, data, __) { return Consumer<SpotifyDI>(builder: (_, data, __) {
@ -23,6 +90,8 @@ class _PlaylistViewState extends State<PlaylistView> {
.all(), .all(),
builder: (context, snapshot) { builder: (context, snapshot) {
List<Track> tracks = snapshot.data?.toList() ?? []; List<Track> tracks = snapshot.data?.toList() ?? [];
TextStyle tableHeadStyle =
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
return Column( return Column(
children: [ children: [
Row( Row(
@ -72,39 +141,49 @@ class _PlaylistViewState extends State<PlaylistView> {
? const CircularProgressIndicator.adaptive() ? const CircularProgressIndicator.adaptive()
: Expanded( : Expanded(
child: Scrollbar( child: Scrollbar(
isAlwaysShown: true, child: ListView(
child: ListView.builder(
itemCount: tracks.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return Column(
children: [ children: [
TrackButton( SingleChildScrollView(
index: "#", child: Table(
trackName: "Title", columnWidths: const {
artists: ["Artist"], 0: FixedColumnWidth(40),
album: "Album", 1: FlexColumnWidth(),
playback_time: "Time"), 2: FlexColumnWidth(),
const Divider() 3: FixedColumnWidth(40),
},
children: [
TableRow(
children: [
TableCell(
child: Text(
"#",
textAlign: TextAlign.center,
style: tableHeadStyle,
)),
TableCell(
child: Text(
"Title",
style: tableHeadStyle,
)),
TableCell(
child: Text(
"Album",
style: tableHeadStyle,
)),
TableCell(
child: Text(
"Time",
textAlign: TextAlign.center,
style: tableHeadStyle,
)),
], ],
); ),
} ...trackToTableRow(tracks),
Track track = tracks[index - 1]; ],
return TrackButton( ),
index: (index - 1).toString(), ),
thumbnail_url: track ],
.album?.images?.last.url ?? ),
"https://i.scdn.co/image/ab67616d00001e02b993cba8ff7d0a8e9ee18d46",
trackName: track.name!,
artists: track.artists!
.map((e) => e.name!)
.toList(),
album: track.album!.name!,
playback_time: track.duration!.inMinutes
.toString(),
onTap: () {},
);
}),
), ),
), ),
], ],

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/provider/Auth.dart';
class Settings extends StatefulWidget {
const Settings({Key? key}) : super(key: key);
@override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
iconTheme: Theme.of(context).iconTheme,
title: const Text(
"Settings",
),
centerTitle: true,
titleTextStyle: Theme.of(context).textTheme.headline4,
),
body: Column(
children: [
Builder(builder: (context) {
var auth = context.read<Auth>();
return ElevatedButton(
child: const Text("Logout"),
onPressed: () async {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
await localStorage.clear();
auth.logout();
Navigator.of(context).pop();
},
);
})
],
),
);
}
}

View File

@ -1,77 +0,0 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
class TrackButton extends StatefulWidget {
final String index;
final String trackName;
final List<String> artists;
final String album;
final String playback_time;
final String? thumbnail_url;
final void Function()? onTap;
TrackButton({
required this.index,
required this.trackName,
required this.artists,
required this.album,
required this.playback_time,
this.thumbnail_url,
this.onTap,
});
@override
_TrackButtonState createState() => _TrackButtonState();
}
class _TrackButtonState extends State<TrackButton> {
@override
Widget build(BuildContext context) {
return Material(
child: InkWell(
onTap: widget.onTap,
child: Ink(
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Text(
widget.index,
style: const TextStyle(fontSize: 20),
),
const SizedBox(width: 15),
if (widget.thumbnail_url != null)
CachedNetworkImage(
imageUrl: widget.thumbnail_url!,
maxHeightDiskCache: 50,
maxWidthDiskCache: 50,
),
const SizedBox(width: 15),
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.trackName,
textAlign: TextAlign.justify,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 17),
),
Text(widget.artists.join(", "))
],
),
),
],
),
const SizedBox(width: 15),
Text(widget.album),
const SizedBox(width: 15),
Text(widget.playback_time)
],
),
),
),
);
}
}

View File

@ -0,0 +1,3 @@
String zeroPadNumStr(int input) {
return input < 10 ? "0$input" : input.toString();
}

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/Home.dart'; import 'package:spotube/components/Home.dart';
import 'package:spotube/models/LocalStorageKeys.dart';
import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/Auth.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyDI.dart';
@ -18,8 +20,36 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider<Auth>(create: (context) => Auth()), ChangeNotifierProvider<Auth>(create: (context) => Auth()),
ChangeNotifierProvider<SpotifyDI>(create: (context) { ChangeNotifierProvider<SpotifyDI>(create: (context) {
Auth authState = Provider.of<Auth>(context, listen: false); Auth authState = Provider.of<Auth>(context, listen: false);
return SpotifyDI(SpotifyApi(SpotifyApiCredentials( return SpotifyDI(
authState.cliendId, authState.clientSecret))); SpotifyApi(
SpotifyApiCredentials(
authState.clientId,
authState.clientSecret,
accessToken: authState.accessToken,
refreshToken: authState.refreshToken,
expiration: authState.expiration,
scopes: spotifyScopes,
),
onCredentialsRefreshed: (credentials) async {
SharedPreferences localStorage =
await SharedPreferences.getInstance();
localStorage.setString(
LocalStorageKeys.refreshToken,
credentials.refreshToken!,
);
localStorage.setString(
LocalStorageKeys.accessToken,
credentials.accessToken!,
);
localStorage.setString(
LocalStorageKeys.clientId, credentials.clientId!);
localStorage.setString(
LocalStorageKeys.clientSecret,
credentials.clientSecret!,
);
},
),
);
}), }),
ChangeNotifierProvider<Playback>(create: (context) => Playback()), ChangeNotifierProvider<Playback>(create: (context) => Playback()),
], ],

View File

@ -0,0 +1,7 @@
abstract class LocalStorageKeys {
static String clientId = 'client_id';
static String clientSecret = 'client_secret';
static String accessToken = 'access_token';
static String refreshToken = 'refresh_token';
static String expiration = " expiration";
}

View File

@ -3,10 +3,17 @@ import 'package:flutter/cupertino.dart';
class Auth with ChangeNotifier { class Auth with ChangeNotifier {
String? _clientId; String? _clientId;
String? _clientSecret; String? _clientSecret;
String? _accessToken;
String? _refreshToken;
DateTime? _expiration;
bool _isLoggedIn = false; bool _isLoggedIn = false;
String? get cliendId => _clientId; String? get clientId => _clientId;
String? get clientSecret => _clientSecret; String? get clientSecret => _clientSecret;
String? get accessToken => _accessToken;
String? get refreshToken => _refreshToken;
DateTime? get expiration => _expiration;
bool get isLoggedIn => _isLoggedIn; bool get isLoggedIn => _isLoggedIn;
void setAuthState({ void setAuthState({
@ -14,17 +21,34 @@ class Auth with ChangeNotifier {
bool safe = true, bool safe = true,
String? clientId, String? clientId,
String? clientSecret, String? clientSecret,
String? refresh_token, String? refreshToken,
String? access_token, String? accessToken,
DateTime? expiration,
}) { }) {
if (safe) { if (safe) {
if (clientId != null) _clientId = clientId; if (clientId != null) _clientId = clientId;
if (clientSecret != null) _clientSecret = clientSecret; if (clientSecret != null) _clientSecret = clientSecret;
if (isLoggedIn != null) _isLoggedIn = isLoggedIn; if (isLoggedIn != null) _isLoggedIn = isLoggedIn;
if (refreshToken != null) _refreshToken = refreshToken;
if (accessToken != null) _accessToken = accessToken;
if (expiration != null) _expiration = expiration;
} else { } else {
_clientId = clientId; _clientId = clientId;
_clientSecret = clientSecret; _clientSecret = clientSecret;
_accessToken = accessToken;
_refreshToken = refreshToken;
_expiration = expiration;
} }
notifyListeners(); notifyListeners();
} }
logout() {
_clientId = null;
_clientSecret = null;
_accessToken = null;
_refreshToken = null;
_expiration = null;
_isLoggedIn = false;
notifyListeners();
}
} }

View File

@ -68,8 +68,8 @@ flutter:
uses-material-design: true uses-material-design: true
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: assets:
# - images/a_dot_burr.jpeg - assets/
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see