Skip to content

Commit 9ff11b4

Browse files
committed
✨ Added user Qr code generator/Scanner on profile page
1 parent 9d02290 commit 9ff11b4

File tree

6 files changed

+374
-1
lines changed

6 files changed

+374
-1
lines changed

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ android {
3434
defaultConfig {
3535
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
3636
applicationId "com.thealphamerc.flutter_twitter_clone_dev"
37-
minSdkVersion 16
37+
minSdkVersion 20
3838
targetSdkVersion 29
3939
versionCode flutterVersionCode.toInteger()
4040
versionName flutterVersionName

lib/page/profile/profilePage.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter_twitter_clone/page/profile/qrCode/scanner.dart';
12
import 'package:firebase_dynamic_links/firebase_dynamic_links.dart';
23
import 'package:flutter/material.dart';
34
import 'package:flutter_twitter_clone/helper/constant.dart';
@@ -66,6 +67,9 @@ class _ProfilePageState extends State<ProfilePage>
6667
onSelected: (d) {
6768
if (d.title == "Share") {
6869
shareProfile(context);
70+
} else if (d.title == "QR code") {
71+
Navigator.push(context,
72+
ScanScreen.getRoute(authstate.profileUserModel));
6973
}
7074
},
7175
itemBuilder: (BuildContext context) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import 'dart:math';
2+
3+
import 'package:flutter/material.dart';
4+
5+
/// An indicator showing the currently selected page of a PageController
6+
class DotsIndicator extends AnimatedWidget {
7+
DotsIndicator({
8+
this.controller,
9+
this.itemCount,
10+
this.onPageSelected,
11+
this.color: Colors.white,
12+
}) : super(listenable: controller);
13+
14+
/// The PageController that this DotsIndicator is representing.
15+
final PageController controller;
16+
17+
/// The number of items managed by the PageController
18+
final int itemCount;
19+
20+
/// Called when a dot is tapped
21+
final ValueChanged<int> onPageSelected;
22+
23+
/// The color of the dots.
24+
///
25+
/// Defaults to `Colors.white`.
26+
final Color color;
27+
28+
// The base size of the dots
29+
static const double _kDotSize = 6.0;
30+
31+
// The increase in the size of the selected dot
32+
static const double _kMaxZoom = 1.5;
33+
34+
// The distance between the center of each dot
35+
static const double _kDotSpacing = 16.0;
36+
37+
Widget _buildDot(int index) {
38+
double selectedness = Curves.easeOut.transform(
39+
max(
40+
0.0,
41+
1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
42+
),
43+
);
44+
double zoom = 1.0 + (_kMaxZoom - 1.0) * selectedness;
45+
return new Container(
46+
width: _kDotSpacing,
47+
child: new Center(
48+
child: new Material(
49+
color: color,
50+
type: MaterialType.circle,
51+
child: new Container(
52+
width: _kDotSize * zoom,
53+
height: _kDotSize * zoom,
54+
child: new InkWell(
55+
onTap: () => onPageSelected(index),
56+
),
57+
),
58+
),
59+
),
60+
);
61+
}
62+
63+
Widget build(BuildContext context) {
64+
return new Row(
65+
mainAxisAlignment: MainAxisAlignment.center,
66+
children: new List<Widget>.generate(itemCount, _buildDot),
67+
);
68+
}
69+
}

lib/page/profile/qrCode/scanner.dart

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import 'dart:io';
2+
import 'dart:math';
3+
import 'dart:ui' as ui;
4+
import 'dart:typed_data';
5+
import 'dot_indicator.dart';
6+
import 'package:flutter/material.dart';
7+
import 'package:flutter/rendering.dart';
8+
import 'package:flutter/services.dart';
9+
import 'package:qr_flutter/qr_flutter.dart';
10+
import 'package:path_provider/path_provider.dart';
11+
import 'package:flutter_twitter_clone/model/user.dart';
12+
import 'package:qr_code_scanner/qr_code_scanner.dart';
13+
import 'package:flutter_twitter_clone/helper/utility.dart';
14+
import 'package:flutter_twitter_clone/widgets/customWidgets.dart';
15+
16+
class ScanScreen extends StatefulWidget {
17+
final UserModel user;
18+
19+
const ScanScreen({Key key, this.user}) : super(key: key);
20+
static MaterialPageRoute getRoute(UserModel user) {
21+
return new MaterialPageRoute(
22+
builder: (BuildContext context) => ScanScreen(user: user));
23+
}
24+
25+
@override
26+
_ScanState createState() => new _ScanState();
27+
}
28+
29+
class _ScanState extends State<ScanScreen> with SingleTickerProviderStateMixin {
30+
final GlobalKey qrKey = GlobalKey();
31+
PageController pageController;
32+
double pageIndex = 0;
33+
Barcode result;
34+
bool isFound = false;
35+
QRViewController controller;
36+
GlobalKey globalKey = new GlobalKey();
37+
@override
38+
void initState() {
39+
super.initState();
40+
pageController = PageController()..addListener(pageListener);
41+
}
42+
43+
@override
44+
void reassemble() {
45+
super.reassemble();
46+
if (Platform.isAndroid) {
47+
controller.pauseCamera();
48+
} else if (Platform.isIOS) {
49+
controller.resumeCamera();
50+
}
51+
}
52+
53+
void pageListener() {
54+
setState(() {
55+
pageIndex = pageController.page;
56+
});
57+
}
58+
59+
_capturePng() async {
60+
try {
61+
// isLoading.value = true;
62+
RenderRepaintBoundary boundary =
63+
globalKey.currentContext.findRenderObject();
64+
ui.Image image = await boundary.toImage(pixelRatio: 3.0);
65+
ByteData byteData =
66+
await image.toByteData(format: ui.ImageByteFormat.png);
67+
var path = await _localPath + "/${DateTime.now().toIso8601String()}.png";
68+
await writeToFile(byteData, path);
69+
70+
shareFile([path], text: "");
71+
// isLoading.value = false;
72+
} catch (e) {
73+
print(e);
74+
}
75+
}
76+
77+
Future<String> get _localPath async {
78+
final directory = await getApplicationDocumentsDirectory();
79+
80+
return directory.path;
81+
}
82+
83+
Future<File> writeToFile(ByteData data, String path) {
84+
final buffer = data.buffer;
85+
return new File(path).writeAsBytes(
86+
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
87+
}
88+
89+
@override
90+
Widget build(BuildContext context) {
91+
return Scaffold(
92+
body: SafeArea(
93+
child: Stack(
94+
children: <Widget>[
95+
PageView.builder(
96+
controller: pageController,
97+
itemCount: 2,
98+
itemBuilder: (BuildContext context, int index) {
99+
if (index == 0) {
100+
// return QrCode(
101+
// user: widget.user,
102+
// globalKey: globalKey,
103+
// );
104+
return QRView(
105+
key: qrKey,
106+
onQRViewCreated: _onQRViewCreated,
107+
overlay: QrScannerOverlayShape(
108+
borderRadius: 40,
109+
borderColor: Theme.of(context).colorScheme.onPrimary,
110+
borderWidth: 10,
111+
borderLength: MediaQuery.of(context).size.width * .5),
112+
);
113+
} else {
114+
return QrCode(user: widget.user, globalKey: globalKey);
115+
}
116+
},
117+
),
118+
Align(
119+
alignment: Alignment.topLeft,
120+
child: Row(
121+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
122+
children: [
123+
BackButton(color: Theme.of(context).colorScheme.onPrimary),
124+
AnimatedContainer(
125+
duration: Duration(microseconds: 200),
126+
child: pageIndex == 0
127+
? SizedBox.shrink()
128+
: IconButton(
129+
onPressed: () async {
130+
if (pageIndex == 1) {
131+
_capturePng();
132+
}
133+
},
134+
icon: Icon(
135+
Icons.share_outlined,
136+
color: Theme.of(context).colorScheme.onPrimary,
137+
),
138+
),
139+
),
140+
],
141+
),
142+
),
143+
_controls()
144+
],
145+
),
146+
),
147+
);
148+
}
149+
150+
Widget _controls() {
151+
return Align(
152+
alignment: Alignment.bottomCenter,
153+
child: Container(
154+
height: 50,
155+
child: DotsIndicator(
156+
controller: pageController,
157+
color: Theme.of(context).colorScheme.onPrimary,
158+
itemCount: 2,
159+
onPageSelected: (int page) {
160+
pageController.animateToPage(
161+
page,
162+
duration: Duration(milliseconds: 750),
163+
curve: Curves.ease,
164+
);
165+
},
166+
),
167+
),
168+
);
169+
}
170+
171+
void _onQRViewCreated(QRViewController controller) {
172+
this.controller = controller;
173+
controller.scannedDataStream.listen((scanData) {
174+
if (isFound) {
175+
return;
176+
}
177+
setState(() {
178+
result = scanData;
179+
});
180+
if (result.code.contains("fwitter/profile/")) {
181+
isFound = true;
182+
Navigator.pop(context);
183+
var userId = result.code.split("/")[2];
184+
Navigator.of(context).pushNamed('/ProfilePage/' + userId);
185+
}
186+
});
187+
}
188+
189+
@override
190+
void dispose() {
191+
controller?.dispose();
192+
pageController.dispose();
193+
super.dispose();
194+
}
195+
}
196+
197+
class QrCode extends StatefulWidget {
198+
const QrCode({Key key, this.user, this.globalKey}) : super(key: key);
199+
final UserModel user;
200+
final GlobalKey globalKey;
201+
@override
202+
_QrCodeState createState() => _QrCodeState();
203+
}
204+
205+
class _QrCodeState extends State<QrCode> {
206+
Color color = Color(0xff07B7A6);
207+
Color get randomColor {
208+
final colors = <Color>[
209+
Color(0xffFF7878),
210+
Color(0xffFFA959),
211+
Color(0xff83DA2D),
212+
Color(0xff1FE2D7),
213+
Color(0xffC13E6B),
214+
Color(0xffFF7878),
215+
Color(0xff07B7A6),
216+
Color(0xff1F7ACD),
217+
Color(0xffBB78FF),
218+
Color(0xffF14CD7),
219+
Color(0xffFF5757),
220+
Color(0xff28B446),
221+
// Color(0xffffffff)
222+
];
223+
224+
Random ran = Random.secure();
225+
return colors[ran.nextInt(11)];
226+
}
227+
228+
@override
229+
Widget build(BuildContext context) {
230+
return Container(
231+
color: Theme.of(context).dividerColor.withOpacity(.6),
232+
alignment: Alignment.center,
233+
child: InkWell(
234+
onTap: () {
235+
color = randomColor;
236+
setState(() {});
237+
},
238+
child: RepaintBoundary(
239+
key: widget.globalKey,
240+
child: Stack(
241+
alignment: Alignment.center,
242+
children: [
243+
ClipRRect(
244+
borderRadius: BorderRadius.circular(6),
245+
child: Container(
246+
decoration: BoxDecoration(
247+
color: color,
248+
border: Border.all(
249+
color: Theme.of(context).colorScheme.onPrimary,
250+
width: 4,
251+
),
252+
borderRadius: BorderRadius.circular(10)),
253+
padding: EdgeInsets.all(22),
254+
child: QrImage(
255+
data: "fwitter/profile/${widget.user.userId}",
256+
embeddedImageStyle:
257+
QrEmbeddedImageStyle(size: Size(60, 60)),
258+
version: QrVersions.auto,
259+
// foregroundColor: Theme.of(context).colorScheme.onPrimary,
260+
backgroundColor: color,
261+
size: MediaQuery.of(context).size.width * .7,
262+
),
263+
),
264+
),
265+
customImage(
266+
context,
267+
widget.user.profilePic,
268+
height: 50,
269+
),
270+
],
271+
),
272+
),
273+
),
274+
);
275+
}
276+
}

pubspec.lock

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,27 @@ packages:
590590
url: "https://pub.dartlang.org"
591591
source: hosted
592592
version: "1.4.4"
593+
qr:
594+
dependency: transitive
595+
description:
596+
name: qr
597+
url: "https://pub.dartlang.org"
598+
source: hosted
599+
version: "1.3.0"
600+
qr_code_scanner:
601+
dependency: "direct main"
602+
description:
603+
name: qr_code_scanner
604+
url: "https://pub.dartlang.org"
605+
source: hosted
606+
version: "0.3.5"
607+
qr_flutter:
608+
dependency: "direct main"
609+
description:
610+
name: qr_flutter
611+
url: "https://pub.dartlang.org"
612+
source: hosted
613+
version: "3.2.0"
593614
quiver:
594615
dependency: transitive
595616
description:

0 commit comments

Comments
 (0)