Working project
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'shorten_service.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Home(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
const Home({super.key});
|
||||
|
||||
@override
|
||||
State<Home> createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
late final TextEditingController controller;
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
String? shortenedUrl;
|
||||
bool loading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
controller.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text(
|
||||
'Url shortner',
|
||||
style: TextStyle(fontSize: 24),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const SizedBox(width: 30),
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 300),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
onFieldSubmitted: loading ? null : (_) => processUrl(),
|
||||
validator: (String? url) {
|
||||
try {
|
||||
if (url == null || url.isEmpty) {
|
||||
return 'No url entered.';
|
||||
}
|
||||
final Uri uri = Uri.parse(url);
|
||||
if (!uri.hasScheme || (!uri.isScheme('HTTP') && !uri.isScheme('HTTPS'))) {
|
||||
throw const FormatException();
|
||||
}
|
||||
return null;
|
||||
} on FormatException {
|
||||
return 'Bad URL';
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.arrow_circle_right_rounded),
|
||||
onPressed: loading ? null : () => processUrl(),
|
||||
),
|
||||
],
|
||||
),
|
||||
AnimatedOpacity(
|
||||
opacity: shortenedUrl == null ? 0.0 : 1.0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 14.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const Text('Short URL:'),
|
||||
InkWell(
|
||||
onTap: shortenedUrl == null ? null : () => launchUrl(Uri.parse(shortenedUrl!)),
|
||||
child: Text(
|
||||
shortenedUrl ?? '',
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 24),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Center(
|
||||
child: AnimatedOpacity(
|
||||
opacity: loading ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: const CircularProgressIndicator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> processUrl() async {
|
||||
try {
|
||||
assert(formKey.currentState != null, 'Could not validate form, formKey currentState is null');
|
||||
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
setState(() => loading = true);
|
||||
final bool isValid = formKey.currentState!.validate();
|
||||
|
||||
if (!isValid) {
|
||||
setState(() => loading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
final String? shortUrl = await shortenUrl(controller.text);
|
||||
if (shortUrl != null) {
|
||||
setState(() {
|
||||
shortenedUrl = shortUrl;
|
||||
loading = false;
|
||||
});
|
||||
} else {
|
||||
// show error
|
||||
setState(() {
|
||||
shortenedUrl = null;
|
||||
loading = false;
|
||||
});
|
||||
}
|
||||
} catch (err, st) {
|
||||
if (kDebugMode) {
|
||||
print(st);
|
||||
print('An error occurred trying to shorten the url: $err');
|
||||
}
|
||||
setState(() => loading = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
BaseOptions dioOptions = BaseOptions(baseUrl: 'http://localhost:8080');
|
||||
|
||||
Future<String?> shortenUrl(String url) async {
|
||||
try {
|
||||
assert(url.isNotEmpty);
|
||||
final Response<String> res = await Dio(dioOptions).post('/', data: <String, String>{'url': url});
|
||||
final Map<String, dynamic> json = jsonDecode(res.data ?? '') as Map<String, dynamic>;
|
||||
print(json);
|
||||
return json['url'] as String?;
|
||||
} catch (err, st) {
|
||||
if (kDebugMode) {
|
||||
print('Encountered an error:');
|
||||
print(st);
|
||||
print(err);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
class ShrinkRay {}
|
||||
Reference in New Issue
Block a user