diff --git a/lib/homepage.dart b/lib/homepage.dart deleted file mode 100644 index 25d13f3..0000000 --- a/lib/homepage.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:flutter/material.dart'; - -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 { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @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 Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.display1, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 5985578..6811ccc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,23 +5,11 @@ import 'serverlist.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { - // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Server pinger', - 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. - primaryColor: Colors.red.shade900 - ), + theme: ThemeData(primaryColor: Colors.red.shade900), home: ServerList(title: 'Server list'), ); } diff --git a/lib/server.dart b/lib/server.dart index dbd21b6..9eb3e2a 100644 --- a/lib/server.dart +++ b/lib/server.dart @@ -1,4 +1,11 @@ -enum ServerStatus { unknown, ok, notok, timeout, dnserror } +enum ServerStatus { + unknown, + ok, + notOk, + timeout, + dnsError, + clientConnectivityIssues +} class Server { String displayName; diff --git a/lib/serverlist.dart b/lib/serverlist.dart index b356329..a25b522 100644 --- a/lib/serverlist.dart +++ b/lib/serverlist.dart @@ -61,36 +61,12 @@ class _ServerListState extends State { @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 Scaffold( appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: SingleChildScrollView( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). mainAxisAlignment: MainAxisAlignment.start, children: _createChildren(), ), @@ -124,8 +100,7 @@ class _ServerListState extends State { labelStyle: TextStyle(fontSize: 18.0), onTap: _addServer, ), - ],*/ - // This trailing comma makes auto-formatting nicer for build methods. + ],*/ // This trailing comma makes auto-formatting nicer for build methods. ); } @@ -133,13 +108,13 @@ class _ServerListState extends State { final _formKey = GlobalKey(); void _addServerForm() { - TextEditingController servername = TextEditingController(); + TextEditingController serverName = TextEditingController(); TextEditingController uri = TextEditingController(); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: Text('Adding a server'), + title: Text('Add server'), content: SingleChildScrollView( child: Form( key: _formKey, @@ -148,15 +123,15 @@ class _ServerListState extends State { children: [ TextFormField( decoration: InputDecoration( - labelText: 'Custom Name', + labelText: 'Display name', hintText: 'Optional', ), - controller: servername, + controller: serverName, ), TextFormField( controller: uri, decoration: InputDecoration( - labelText: 'URI', + labelText: 'URL', hintText: 'https://example.com', ), ), @@ -169,9 +144,9 @@ class _ServerListState extends State { uri.text.isNotEmpty) { _formKey.currentState.save(); _addServer( - servername.text.isEmpty + serverName.text.isEmpty ? uri.text - : servername.text, + : serverName.text, uri.text); } }, @@ -199,14 +174,15 @@ class _ServerListState extends State { }); } - void _modifyServerForm(Server server){ - TextEditingController servername = TextEditingController(text: server.displayName); + void _modifyServerForm(Server server) { + TextEditingController servername = + TextEditingController(text: server.displayName); TextEditingController uri = TextEditingController(text: server.uri); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( - title: Text('Adding a server'), + title: Text('Edit server'), content: SingleChildScrollView( child: Form( key: _formKey, @@ -215,36 +191,36 @@ class _ServerListState extends State { children: [ TextFormField( decoration: InputDecoration( - labelText: 'Custom Name', + labelText: 'Display name', ), controller: servername, ), TextFormField( controller: uri, decoration: InputDecoration( - labelText: 'URI', + labelText: 'URL', ), ), Padding( padding: const EdgeInsets.all(8.0), child: Row(children: [ - RaisedButton( - child: Text("Cancel"), - onPressed: () { - if (_formKey.currentState.validate()) { - _formKey.currentState.save(); - Navigator.of(context).pop(); - } - }, - ), - Expanded(child:Text(' ')), + RaisedButton( + child: Text("Cancel"), + onPressed: () { + if (_formKey.currentState.validate()) { + _formKey.currentState.save(); + Navigator.of(context).pop(); + } + }, + ), + Expanded(child: Text(' ')), RaisedButton( child: Text("Confirm"), onPressed: () { if (_formKey.currentState.validate()) { _formKey.currentState.save(); setState(() { - server.displayName=servername.text; + server.displayName = servername.text; server.uri = uri.text; }); Navigator.of(context).pop(); @@ -262,6 +238,7 @@ class _ServerListState extends State { }, ); } + List _createChildren() { List list = new List.generate(servers.length, (int index) { Widget info = Row(children: [ @@ -292,81 +269,76 @@ class _ServerListState extends State { border: Border(top: BorderSide(color: Colors.grey))), padding: EdgeInsets.fromLTRB(0, 0, 0, 5), ), - Column(children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - servers[index].uri, - textAlign: TextAlign.left, + Row(children: [ + Expanded( + child: Text( + _truncateURI(servers[index].uri), + textAlign: TextAlign.left, + )), + IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.edit, + color: Colors.grey, ), + onPressed: () { + _modifyServerForm(servers[index]); + }, ), - Row(children: [ - Expanded( - child: IconButton( - alignment: Alignment.centerLeft, - icon: Icon( - Icons.delete_forever, - color: Colors.red, - ), - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('Are you sure you want to delete ' + - servers[index].displayName + - '?'), - content: Form( - key: _formKey, - child: Row( - children: [ - Expanded( - child: FlatButton( - child: Text( - 'Yes', - style: TextStyle( - color: Colors.black, - fontSize: 24, - ), - ), - onPressed: () { - _removeServer(index); - Navigator.of(context).pop(); - }, + IconButton( + alignment: Alignment.center, + icon: Icon( + Icons.delete_forever, + color: Colors.red, + ), + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Are you sure you want to delete ' + + servers[index].displayName + + '?'), + content: Form( + key: _formKey, + child: Row( + children: [ + Expanded( + child: FlatButton( + child: Text( + 'Yes', + style: TextStyle( + color: Colors.black, + fontSize: 24, ), ), - FlatButton( - child: Text( - 'No', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.black, - fontSize: 24, - ), - ), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ], + onPressed: () { + _removeServer(index); + Navigator.of(context).pop(); + }, + ), ), - ), - ); - }, + FlatButton( + child: Text( + 'No', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 24, + ), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + ), ); }, - ), - ), - IconButton( - icon: Icon( - Icons.edit, - color: Colors.grey, - ), - onPressed: () { - _modifyServerForm(servers[index]); - }, - ), - ]), + ); + }, + ), ]), ], ); @@ -422,59 +394,59 @@ class _ServerListState extends State { try { parsedUri = Uri.parse(uri); } catch (FormatException) { - return ServerStatus.dnserror; + return ServerStatus.dnsError; } try { // Check if host is IP address InternetAddress(parsedUri.host); } catch (ArgumentError) /* Host is a DNS name */ { + var v4recs, v6recs; try { - var v4recs = await DnsUtils.lookupRecord(parsedUri.host, RRecordType.A); - var v6recs = - await DnsUtils.lookupRecord(parsedUri.host, RRecordType.AAAA); - var v4 = await _resolveHostname(v4recs, RRecordType.A); - var v6 = await _resolveHostname(v6recs, RRecordType.AAAA); - if (v4 != "" && v6 != "") { - return ServerStatus.dnserror; - } + v4recs = await DnsUtils.lookupRecord(parsedUri.host, RRecordType.A); + v6recs = await DnsUtils.lookupRecord(parsedUri.host, RRecordType.AAAA); } catch (SocketException) { - return ServerStatus.dnserror; + return ServerStatus.clientConnectivityIssues; + } + var v4 = await _resolveHostname(v4recs, RRecordType.A); + var v6 = await _resolveHostname(v6recs, RRecordType.AAAA); + if (v4 == null && v6 == null) { + return ServerStatus.dnsError; } } - try { - return await _checkResponse(parsedUri); - } catch (TimeoutException) { - return ServerStatus.timeout; - } + return await _checkResponse(parsedUri); } Icon _generateIcon(Server server) { var icon, color; switch (server.status) { case ServerStatus.unknown: - icon = Icons.timer; + icon = Icons.cloud_queue; color = Colors.blueGrey.shade400; break; case ServerStatus.ok: - icon = Icons.check_box; + icon = Icons.cloud; color = Colors.green.shade400; break; - case ServerStatus.notok: - icon = Icons.indeterminate_check_box; - color = Colors.red; + case ServerStatus.notOk: + icon = Icons.cloud_off; + color = Colors.red.shade400; break; case ServerStatus.timeout: icon = Icons.timer_off; color = Colors.yellowAccent.shade700; break; - case ServerStatus.dnserror: + case ServerStatus.dnsError: icon = Icons.dns; color = Colors.red.shade400; break; + case ServerStatus.clientConnectivityIssues: + icon = Icons.signal_wifi_off; + color = Colors.red.shade400; + break; default: - icon = Icons.indeterminate_check_box; - color = Colors.red; + icon = Icons.device_unknown; + color = Colors.blueGrey.shade400; break; } return new Icon(icon, color: color); @@ -482,7 +454,7 @@ class _ServerListState extends State { static _resolveHostname(List records, RRecordType type) { String ipAddress; - if (records == null) return ServerStatus.dnserror; + if (records == null) return null; records.forEach((record) { if (ipAddress != null) { @@ -490,24 +462,30 @@ class _ServerListState extends State { } if (ipAddress == null) { - // ignore: unrelated_type_equality_checks - if (record.rType == type) { - ipAddress = record.data; - } + ipAddress = record.data; } }); - if (ipAddress == null) ipAddress = ""; return ipAddress; } static Future _checkResponse(Uri uri) async { - var response = await HttpUtils.getForFullResponse(uri.toString()) - .timeout(new Duration(seconds: 3)); + var response; + try { + response = await HttpUtils.getForFullResponse(uri.toString()) + .timeout(new Duration(seconds: 3)); + } catch (TimeoutException) { + return ServerStatus.timeout; + } if (response.statusCode > 199 && response.statusCode < 300) { return ServerStatus.ok; } else { - return ServerStatus.notok; + return ServerStatus.notOk; } } + + String _truncateURI(String uri) { + if (uri.length < 100) return uri; + return uri.substring(0, 100) + "..."; + } }