Skip to content

Commit 1cfe154

Browse files
author
gopi2401
committed
feat: update theme toggle implementation to require BuildContext for AdaptiveTheme support
1 parent 3441325 commit 1cfe154

File tree

7 files changed

+180
-92
lines changed

7 files changed

+180
-92
lines changed

IMPLEMENTATION_GUIDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ Improved the existing `adaptive_theme` implementation with smooth transitions an
3636
```dart
3737
// Toggle theme
3838
final themeService = ThemeService.to;
39-
await themeService.toggleTheme();
39+
// supply a BuildContext so AdaptiveTheme can be toggled as well
40+
await themeService.toggleTheme(context);
4041
4142
// Check current theme
4243
bool isDark = ThemeService.to.isDarkMode.value;

QUICK_REFERENCE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
// Get the service
1313
final themeService = ThemeService.to;
1414
15-
// Toggle theme
16-
await themeService.toggleTheme();
15+
// Toggle theme (requires a BuildContext when using AdaptiveTheme)
16+
await themeService.toggleTheme(context);
1717
1818
// Check current theme
1919
if (themeService.isDarkMode.value) {

README_NEW_FEATURES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ RecoveryScreen
187187

188188
### Theme Toggle
189189
```dart
190-
ThemeService.to.toggleTheme();
190+
// pass a BuildContext so that the underlying AdaptiveTheme
191+
// instance can be updated as well as the stored flag.
192+
ThemeService.to.toggleTheme(context);
191193
```
192194

193195
### Show Progress

android/app/src/main/kotlin/com/example/insta/MainActivity.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ class MainActivity : FlutterActivity() {
2626
}
2727
}
2828

29+
// If the app is already running and a new share intent arrives,
30+
// forward it to the same handler so Dart receives the shared text.
31+
override fun onNewIntent(intent: Intent) {
32+
super.onNewIntent(intent)
33+
setIntent(intent)
34+
val action = intent.action
35+
val type = intent.type
36+
if (Intent.ACTION_SEND == action && type != null) {
37+
if ("text/plain" == type) {
38+
handleSendText(intent)
39+
}
40+
}
41+
}
42+
2943
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
3044
super.configureFlutterEngine(flutterEngine)
3145

lib/main.dart

Lines changed: 86 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,43 @@ class MyHomePageState extends State<MyHomePage> {
168168
appUpdate();
169169
}
170170

171+
/// Centralises download-button logic so the build method stays clean.
172+
Future<void> _onDownloadPressed() async {
173+
setState(() {
174+
downloading = true;
175+
errorMessage = null;
176+
});
177+
178+
try {
179+
final url = reelController.text.trim();
180+
if (url.isEmpty) {
181+
setState(() {
182+
errorMessage = 'URL cannot be empty';
183+
});
184+
showToast();
185+
} else {
186+
final uri = Uri.tryParse(url);
187+
if (uri == null || !uri.hasAbsolutePath) {
188+
setState(() {
189+
errorMessage = 'Please enter a valid URL';
190+
});
191+
showToast();
192+
} else {
193+
downloadController = Get.put(DistribUrl());
194+
contexts = context;
195+
await downloadController.handleUrl(url);
196+
reelController.clear();
197+
}
198+
}
199+
} catch (e, stackTrace) {
200+
catchInfo(e, stackTrace);
201+
} finally {
202+
setState(() {
203+
downloading = false;
204+
});
205+
}
206+
}
207+
171208
appUpdate() async {
172209
var appVersion = await getAppVersion();
173210
var newVersion = await checkUpdate();
@@ -242,81 +279,65 @@ class MyHomePageState extends State<MyHomePage> {
242279
@override
243280
Widget build(BuildContext context) {
244281
return Scaffold(
245-
appBar: AppBar(leading: const DrawerButton()),
246-
body: Center(
282+
appBar: AppBar(
283+
leading: const DrawerButton(),
284+
title: const Text('Instagram Downloader'),
285+
),
286+
body: Padding(
287+
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
247288
child: Column(
248-
mainAxisAlignment: MainAxisAlignment.center,
289+
crossAxisAlignment: CrossAxisAlignment.stretch,
249290
children: <Widget>[
250-
downloading ? const CupertinoActivityIndicator() : Container(),
251-
Padding(
252-
padding: const EdgeInsets.all(20.0),
253-
child: TextField(
254-
controller: reelController,
255-
decoration: const InputDecoration(hintText: "Url"),
256-
),
291+
const SizedBox(height: 8),
292+
Text(
293+
'Paste an Instagram URL below and tap download',
294+
style: Theme.of(context).textTheme.titleMedium,
295+
textAlign: TextAlign.center,
257296
),
258-
if (errorMessage != null)
259-
Padding(
260-
padding: const EdgeInsets.all(8.0),
261-
child: Text(
262-
errorMessage!,
263-
style: const TextStyle(color: Colors.red),
297+
const SizedBox(height: 16),
298+
TextField(
299+
controller: reelController,
300+
decoration: InputDecoration(
301+
labelText: 'Instagram URL',
302+
border: const OutlineInputBorder(),
303+
hintText: 'https://www.instagram.com/p/xyz',
304+
errorText: errorMessage,
305+
suffixIcon: IconButton(
306+
icon: const Icon(Icons.clear),
307+
onPressed: () => reelController.clear(),
264308
),
265309
),
266-
ElevatedButton(
267-
child: downloading
268-
? const CircularProgressIndicator(color: Colors.white)
269-
: const Text('Download'),
270-
onPressed: () async {
271-
if (!downloading) {
272-
try {
273-
setState(() {
274-
downloading = true;
275-
errorMessage = null;
276-
});
277-
var url = reelController.text.trim();
278-
if (url.isNotEmpty) {
279-
final Uri uri = Uri.parse(url);
280-
if (uri.hasAbsolutePath) {
281-
downloadController = Get.put(DistribUrl());
282-
contexts = context;
283-
await downloadController.handleUrl(url);
284-
reelController.clear();
285-
setState(() {
286-
downloading = false;
287-
});
288-
} else {
289-
setState(() {
290-
downloading = false;
291-
errorMessage = "Please enter valid url";
292-
});
293-
showToast();
294-
}
295-
} else {
296-
setState(() {
297-
downloading = false;
298-
errorMessage = "url not found!. please enter url";
299-
});
300-
showToast();
301-
}
302-
} catch (e, stackTrace) {
303-
setState(() {
304-
downloading = false;
305-
});
306-
catchInfo(e, stackTrace);
307-
}
308-
}
309-
},
310310
),
311-
TextButton(
311+
const SizedBox(height: 20),
312+
ElevatedButton.icon(
313+
icon: downloading
314+
? const SizedBox(
315+
width: 18,
316+
height: 18,
317+
child: CircularProgressIndicator(
318+
color: Colors.white,
319+
strokeWidth: 2,
320+
),
321+
)
322+
: const Icon(Icons.download),
323+
label: Text(downloading ? 'Downloading...' : 'Download'),
324+
style: ElevatedButton.styleFrom(
325+
padding: const EdgeInsets.symmetric(vertical: 14),
326+
),
327+
onPressed: downloading ? null : _onDownloadPressed,
328+
),
329+
const SizedBox(height: 12),
330+
TextButton.icon(
331+
icon: const Icon(Icons.login),
332+
label: const Text('Login to Instagram'),
312333
onPressed: () {
313334
Navigator.push(
314335
context,
315336
MaterialPageRoute(builder: (context) => const InstaLogin()),
316337
);
317338
},
318-
child: const Text('Login'),
319339
),
340+
const Spacer(),
320341
const Additional(),
321342
],
322343
),
@@ -436,7 +457,9 @@ class DrawerWidget extends StatelessWidget {
436457
),
437458
),
438459
onTap: () {
439-
themeService.toggleTheme();
460+
// keep service state in sync and let AdaptiveTheme
461+
// actually switch the UI
462+
themeService.toggleTheme(context);
440463
},
441464
),
442465
],

lib/services/distrib_url.dart

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,43 +10,74 @@ class DistribUrl extends GetxController {
1010

1111
handleUrl(String url) async {
1212
try {
13+
// If the incoming text contains other words (e.g. "Check this out: https://..."),
14+
// extract the first http(s) URL found. Fall back to the original string if none.
15+
final urlMatch = RegExp(r'https?://[^\s]+').firstMatch(url);
16+
final incoming = urlMatch != null ? urlMatch.group(0)!.trim() : url.trim();
17+
1318
// Instagram URL patterns
14-
RegExp ins = RegExp(r'instagram.com');
15-
bool isInstagram = ins.hasMatch(url);
19+
final isInstagram = RegExp(r'instagram\.com|instagr\.am', caseSensitive: false)
20+
.hasMatch(incoming);
1621

17-
// YouTube URL patterns
18-
RegExp you = RegExp(r'youtube.com');
19-
RegExp youm = RegExp(r'youtu.be');
20-
bool isYouTube = you.hasMatch(url);
21-
bool isYouTubeShort = youm.hasMatch(url);
22+
// YouTube URL patterns (check the extracted incoming text)
23+
final isYouTube = RegExp(r'youtube\.com|youtube\.googleapis|youtu\.be|youtube',
24+
caseSensitive: false)
25+
.hasMatch(incoming);
26+
final isYouTubeShort = RegExp(r'youtu\.be|/shorts/', caseSensitive: false)
27+
.hasMatch(incoming);
2228

2329
if (isInstagram) {
2430
instaController = Get.put(InstaDownloadController());
25-
var segments = url.split("/");
26-
if (segments.length > 3) {
27-
var option = segments[3];
28-
if (option == 'p' || option == 'reel') {
29-
await instaController.downloadReal(url, null);
30-
instaController.onClose();
31-
} else if (option == 'stories' && segments.length > 5) {
32-
if (segments[4] == 'highlights') {
33-
await instaController.highlight(segments[5]);
31+
final uri = Uri.tryParse(incoming);
32+
if (uri != null) {
33+
final segments = uri.pathSegments;
34+
35+
// Find which Instagram resource this URL points to
36+
if (segments.isNotEmpty) {
37+
// common: /p/{shortcode}/ or /reel/{shortcode}/
38+
final first = segments[0].toLowerCase();
39+
if (first == 'p' || first == 'reel') {
40+
await instaController.downloadReal(incoming, null);
3441
instaController.onClose();
42+
} else if (first == 'stories' && segments.length >= 3) {
43+
// /stories/{username}/{storyId}
44+
final userId = segments[1];
45+
final storyId = segments[2];
46+
final idMatch = RegExp(r'^(\d+)').firstMatch(storyId);
47+
if (idMatch != null) {
48+
await instaController.stories(userId, idMatch.group(0)!);
49+
instaController.onClose();
50+
}
51+
} else if (segments.contains('highlights')) {
52+
// /stories/highlights/{highlightId} or /{username}/highlights/{id}
53+
final idx = segments.indexOf('highlights');
54+
if (idx + 1 < segments.length) {
55+
final highlightId = segments[idx + 1];
56+
await instaController.highlight(highlightId);
57+
instaController.onClose();
58+
}
3559
} else {
36-
var userId = segments[4];
37-
var storyId = segments[5];
38-
RegExp regExp = RegExp(r'^(\d+)');
39-
var match = regExp.firstMatch(storyId);
40-
if (match != null) {
41-
await instaController.stories(userId, match.group(0)!);
60+
// fallback: try to detect shortcode in the path and attempt download
61+
// e.g. instagram.com/{username}/p/{shortcode} or embedded links
62+
if (incoming.contains('/p/') || incoming.contains('/reel/')) {
63+
await instaController.downloadReal(incoming, null);
4264
instaController.onClose();
4365
}
4466
}
4567
}
68+
} else {
69+
// If parsing failed, attempt naive match for /p/ or /reel/ in the raw text
70+
if (incoming.contains('/p/') || incoming.contains('/reel/')) {
71+
instaController = Get.put(InstaDownloadController());
72+
await instaController.downloadReal(incoming, null);
73+
instaController.onClose();
74+
}
4675
}
4776
} else if (isYouTube || isYouTubeShort) {
4877
ytController = Get.put(YTDownloadController());
49-
ytController.youtube(url);
78+
// pass the cleaned/extracted URL (incoming) so short links and embedded
79+
// text are handled correctly by the YouTube downloader
80+
await ytController.youtube(incoming);
5081
}
5182
} catch (e, stackTrace) {
5283
catchInfo(e, stackTrace);

lib/services/theme_service.dart

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import 'package:adaptive_theme/adaptive_theme.dart';
12
import 'package:get/get.dart';
23
import 'package:flutter/material.dart';
4+
import 'package:adaptive_theme/adaptive_theme.dart';
35
import 'package:shared_preferences/shared_preferences.dart';
46

57
class ThemeService extends GetxService {
@@ -24,10 +26,25 @@ class ThemeService extends GetxService {
2426
_isDarkMode.value = savedTheme;
2527
}
2628

27-
Future<void> toggleTheme() async {
29+
/// Flip the saved boolean and then update both the
30+
/// stored preference and the visible theme. When the app is
31+
/// wrapped in `AdaptiveTheme` we also need to drive that
32+
/// mechanism; previously we relied on `Get.changeTheme` which
33+
/// had no effect because `MaterialApp` was not a
34+
/// `GetMaterialApp`.
35+
///
36+
/// A [BuildContext] is required so that we can look up the
37+
/// `AdaptiveTheme` instance and call its helpers.
38+
Future<void> toggleTheme(BuildContext context) async {
2839
_isDarkMode.toggle();
2940
await _prefs.setBool('isDarkMode', _isDarkMode.value);
30-
Get.changeTheme(_isDarkMode.value ? darkTheme : lightTheme);
41+
42+
// update AdaptiveTheme so the change is visible and persisted
43+
if (_isDarkMode.value) {
44+
AdaptiveTheme.of(context).setDark();
45+
} else {
46+
AdaptiveTheme.of(context).setLight();
47+
}
3148
}
3249

3350
ThemeData get lightTheme {

0 commit comments

Comments
 (0)