diff --git a/lib/main.dart b/lib/main.dart index f9188bd..4936376 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,452 +1,61 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:flutter/material.dart'; -import 'dart:io' show Platform; -import 'package:path/path.dart' as path; -import 'model.dart' as model; -import 'dart:collection'; -import 'dart:convert'; -import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'model.dart'; +import 'views/profilemgrview.dart'; -typedef start_cwtch_function = Void Function(Pointer str, Int32 length); -typedef StartCwtchFn = void Function(Pointer dir, int len); +void main() => runApp(Flwtch()); -typedef access_cwtch_eventbus_function = Void Function(); -typedef NextEventFn = void Function(); +class Flwtch extends StatefulWidget { + final Key flwtch = GlobalKey(); - -typedef get_json_blob_void_function = Pointer Function(); -typedef GetJsonBlobVoidFn = Pointer Function(); - -typedef get_json_blob_string_function = Pointer Function(Pointer str, Int32 length); -typedef GetJsonBlobStringFn = Pointer Function(Pointer str,int len); - - -class ProfileModel { - String onion; - String nickname; - String creationDate; - HashMap contacts; + @override + FlwtchState createState() => FlwtchState(); } -class ContactModel { - String onion; - String nickname; - bool isGroup; - bool isBlocked; - String status; - List messages; +class FlwtchState extends State { + final TextStyle biggerFont = const TextStyle(fontSize: 18); - ContactModel({this.onion, this.nickname, this.status}); - -} - -class MessageModel { - String from; - String message; - String timestamp; -} - - -void main() => runApp(MyApp()); - - -class MyApp extends StatelessWidget { - // This widget is the root of your application. + DynamicLibrary library; + AppModel appStatus; @override Widget build(BuildContext context) { - - var library = DynamicLibrary.open("libCwtch.so"); + library = DynamicLibrary.open("libCwtch.so"); + appStatus = AppModel(library: library); var startCwtchC = library.lookup>("StartCwtch"); + // ignore: non_constant_identifier_names final StartCwtch = startCwtchC.asFunction(); - var cwtchDir = "/home/sarah/.cwtch/dev/"; + var cwtchDir = "/home/erinn/.cwtch/dev/"; StartCwtch(Utf8.toUtf8(cwtchDir), cwtchDir.length); - - return MaterialApp( - title: 'Flutter Demo', - 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. - primarySwatch: Colors.blue, - // This makes the visual density adapt to the platform that you run - // the app on. For desktop platforms, the controls will be smaller and - // closer together (more dense) than on mobile platforms. - visualDensity: VisualDensity.adaptivePlatformDensity, - primaryColor: Color(0xFF4B3557), - canvasColor: Color(0xFFB09CBC), - accentColor: Color(0xFFD01972), - ), - home:RandomWords(title: 'Cwtch App', library:library, appStatus: model.AppModel(library: library)), - ); - } -} - - -class RandomWords extends StatefulWidget { - - RandomWords({Key key, this.title, this.library, this.appStatus}) : 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; - final DynamicLibrary library; - final model.AppModel appStatus; - - @override - _RandomWordsState createState() => _RandomWordsState(); -} - -class _RandomWordsState extends State { - HashMap _profiles; - String currentlySelectedProfile = ""; - String currentlySelectedConversation = ""; - - // example stuff - final TextStyle _biggerFont = const TextStyle(fontSize: 18); - - - @override - Widget build(BuildContext context) { - - if (_profiles == null) { - _profiles = new HashMap(); - } - - if (_profiles.length < 1) { - - var getProfilesC = widget.library.lookup>("GetProfiles"); - final GetProfiles = getProfilesC.asFunction(); - - - - Pointer jsonProfilesBytes = GetProfiles(); - String jsonProfiles = Utf8.fromUtf8(jsonProfilesBytes); - print(jsonProfiles); - - Map profiles = jsonDecode(jsonProfiles); - - profiles.forEach((onion,nick) { - ProfileModel profile1 = new ProfileModel(); - profile1.onion = onion; - profile1.nickname = nick; - profile1.creationDate = "4 jan 2020"; - - profile1.contacts = new HashMap(); - - - - - _profiles.putIfAbsent(profile1.onion, () => profile1); - }); - - // ProfileModel profile1 = new ProfileModel(); - // profile1.onion = "xyzonion"; - // profile1.nickname = "Erinn"; - // profile1.creationDate = "4 jan 2020"; - // profile1.contacts = new HashMap(); - // ProfileModel profile2 = new ProfileModel(); - // profile2.onion = "baphonion"; - // profile2.nickname = "Baphomet"; - // profile2.creationDate = "6 jan 2020"; - // profile2.contacts = new HashMap(); - // - // ContactModel contactErinn = new ContactModel(); - // contactErinn.onion = profile1.onion; - // contactErinn.nickname = profile1.nickname; - // MessageModel m0 = new MessageModel(); - // m0.from = contactErinn.onion; - // m0.message = "hello baphomet!!"; - // m0.timestamp = "7 jan 2020"; - // MessageModel m1 = new MessageModel(); - // m1.from = profile2.onion; - // m1.message = "well hi there! can't believe this worked!"; - // m1.timestamp = "7 jan 2020"; - // contactErinn.messages = []; - // contactErinn.messages.add(m0); - // contactErinn.messages.add(m1); - - // profile2.contacts.putIfAbsent(contactErinn.onion, () => contactErinn); - // _profiles.putIfAbsent(profile1.onion, () => profile1); - // _profiles.putIfAbsent(profile2.onion, () => profile2); - } - - return Scaffold ( - appBar: AppBar( - title: Text('Profiles'), - actions: [ - IconButton(icon: Icon(Icons.list)), - ], - ), - body: _buildProfileManager(),//_buildSuggestions(), - ); - } - - - void _pushContactList() { - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Contacts for ' + currentlySelectedProfile), - ), - body: _buildContactList(), - ); - }, - ), - ); - } - - Widget _buildContactList() { - - var getContactsC = widget.library.lookup>("GetContacts"); - final GetContacts = getContactsC.asFunction(); - - - - - return StreamBuilder( - stream: widget.appStatus.contactEvents(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - - - - Pointer jsonContactBytes = GetContacts(Utf8.toUtf8(currentlySelectedProfile), currentlySelectedProfile.length); - String jsonContacts = Utf8.fromUtf8(jsonContactBytes); - print(jsonContacts); - List contacts = jsonDecode(jsonContacts); - - Map _contacts = new HashMap(); - contacts.forEach((onion) { - _contacts.putIfAbsent(onion['onion'], () => ContactModel(onion: onion['onion'], nickname: onion['name'], status: onion['status'])); - }); - - // Map _contacts = _profiles[currentlySelectedProfile].contacts; - final tiles = _contacts.values.map( - (ContactModel contact) { - return ListTile( - title: Text( - contact.nickname, - style: _biggerFont, - ), - subtitle: Text(contact.status), - onTap: () { - setState(() { - - currentlySelectedConversation = contact.onion; - _pushMessageView(); - }); - }, - ); - }, - ); - - final divided = ListTile.divideTiles( - context: context, - tiles: tiles, - ).toList(); - - return ListView(children: divided); - }, - ); - } - - Widget _pushMessageView() { - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Convo: ' + currentlySelectedConversation), - ), - body: _buildMessageView(), - ); - }, - ), - ); - } - - String getNick(String profile, String contact) { - return contact == profile ? "me" : _profiles[profile].contacts[contact].nickname; - } - - Widget _buildMessageView() { - List _contacts = _profiles[currentlySelectedProfile].contacts[currentlySelectedConversation].messages; - final tiles = _contacts.map( - (MessageModel m) { - return ListTile( - title: Text( - getNick(currentlySelectedProfile, m.from), + return Provider( + create: (_) => this, + child: MaterialApp( + title: 'Flutter Demo', + 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. + primarySwatch: Colors.blue, + // This makes the visual density adapt to the platform that you run + // the app on. For desktop platforms, the controls will be smaller and + // closer together (more dense) than on mobile platforms. + visualDensity: VisualDensity.adaptivePlatformDensity, + primaryColor: Color(0xFF4B3557), + canvasColor: Color(0xFFB09CBC), + accentColor: Color(0xFFD01972), ), - subtitle: Text( - m.message, - style: _biggerFont, - ), - ); - }, - ); - - final divided = ListTile.divideTiles( - context: context, - tiles: tiles, - ).toList(); - - return ListView(children: divided); + home: ProfileMgrView(), + )); } - - Widget _buildProfileManager() { - final tiles = _profiles.values.map( - (ProfileModel profile) { - return ListTile( - title: Text( - profile.nickname, - style: _biggerFont, - ), - subtitle: Text(profile.onion), - onTap: () { - setState(() { - - var selectProfileC = widget.library.lookup>("SelectProfile"); - final SelectProfile = selectProfileC.asFunction(); - - SelectProfile(Utf8.toUtf8(profile.onion), profile.onion.length); - - currentlySelectedConversation = ""; - currentlySelectedProfile = profile.onion; - _pushContactList(); - }); - }, - ); - }, - ); - - final divided = ListTile.divideTiles( - context: context, - tiles: tiles, - ).toList(); - - return ListView(children: divided); - } - -} - - - -class MyHomePage extends StatefulWidget { - MyHomePage({Key key, this.title, this.library, this.appStatus}) : 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; - final DynamicLibrary library; - final model.AppModel appStatus; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - - - var nextEventC = widget.library.lookup>("NextEvent"); - final NextEvent = nextEventC.asFunction(); - - - // 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. - NextEvent(); - _counter = 1; - }); - } - - @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: [ - StreamBuilder( - stream: widget.appStatus.torStatus(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - return Text( - snapshot.hasData ? - snapshot.data : "Tor not yet Connected", - style: Theme - .of(context) - .textTheme - .headline4, - ); - }, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} +} \ No newline at end of file diff --git a/lib/model.dart b/lib/model.dart index 4343218..dd9a2b1 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -1,21 +1,44 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'dart:async'; -import 'dart:io'; +import 'dart:collection'; + +//////////////////// +/// UI State /// +//////////////////// + +class ProfileModel { + String onion; + String nickname; + String creationDate; + HashMap contacts; +} + +class ContactModel { + String onion; + String nickname; + bool isGroup; + bool isBlocked; + String status; + + ContactModel({this.onion, this.nickname, this.status}); +} + +///////////// +/// ACN /// +///////////// typedef acn_events_function = Pointer Function(); typedef ACNEventsFn = Pointer Function(); - - class AppModel { - final DynamicLibrary library; AppModel({this.library}); Stream contactEvents() async* { var acnEventsC = library.lookup>( "ContactEvents"); + // ignore: non_constant_identifier_names final ContactEvents = acnEventsC.asFunction(); while (true) { @@ -34,6 +57,7 @@ class AppModel { Stream torStatus() async* { var acnEventsC = library.lookup>( "ACNEvents"); + // ignore: non_constant_identifier_names final ACNEvents = acnEventsC.asFunction(); while (true) { @@ -46,4 +70,28 @@ class AppModel { } } } -} \ No newline at end of file +} + +///////////////////// +/// Cwtch API /// +///////////////////// + +typedef start_cwtch_function = Void Function(Pointer str, Int32 length); +typedef StartCwtchFn = void Function(Pointer dir, int len); + +typedef access_cwtch_eventbus_function = Void Function(); +typedef NextEventFn = void Function(); + +typedef get_json_blob_void_function = Pointer Function(); +typedef GetJsonBlobVoidFn = Pointer Function(); + +typedef get_json_blob_string_function = Pointer Function(Pointer str, Int32 length); +typedef GetJsonBlobStringFn = Pointer Function(Pointer str,int len); + +//func NumMessages(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int) (n C.int) { +typedef get_int_from_str_str_function = Int32 Function(Pointer, Int32, Pointer, Int32); +typedef GetIntFromStrStrFn = int Function(Pointer, int, Pointer, int); + +//func GetMessage(profile_ptr *C.char, profile_len C.int, handle_ptr *C.char, handle_len C.int, message_index C.int) *C.char { +typedef get_json_blob_from_str_str_int_function = Pointer Function(Pointer, Int32, Pointer, Int32, Int32); +typedef GetJsonBlobFromStrStrIntFn = Pointer Function(Pointer, int, Pointer, int, int); diff --git a/lib/views/contactsview.dart b/lib/views/contactsview.dart new file mode 100644 index 0000000..c24e4d0 --- /dev/null +++ b/lib/views/contactsview.dart @@ -0,0 +1,96 @@ +import 'dart:collection'; +import 'dart:convert'; +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../main.dart'; +import 'messageview.dart'; +import '../model.dart'; + +class ContactsView extends StatefulWidget { + const ContactsView({Key key, this.flwtch, this.profileOnion}) : super(key: key); + final GlobalKey flwtch; + final String profileOnion; + + @override + _ContactsViewState createState() => _ContactsViewState(); +} + +class _ContactsViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Contacts for ' + widget.profileOnion), + ), + body: _buildContactList(), + ); + } + + + Widget _buildContactList() { + var getContactsC = Provider.of(context).library.lookup>("GetContacts"); + // ignore: non_constant_identifier_names + final GetContacts = getContactsC.asFunction(); + + return StreamBuilder( + stream: Provider.of(context).appStatus.contactEvents(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + Pointer jsonContactBytes = GetContacts(Utf8.toUtf8(widget.profileOnion), widget.profileOnion.length); + String jsonContacts = Utf8.fromUtf8(jsonContactBytes); + print(jsonContacts); + List contacts = jsonDecode(jsonContacts); + + Map _contacts = new HashMap(); + contacts.forEach((onion) { + _contacts.putIfAbsent(onion['onion'], () => ContactModel(onion: onion['onion'], nickname: onion['name'], status: onion['status'])); + }); + + // Map _contacts = _profiles[currentlySelectedProfile].contacts; + final tiles = _contacts.values.map( + (ContactModel contact) { + return ListTile( + title: Text( + contact.nickname, + style: Provider.of(context).biggerFont, + ), + subtitle: Text(contact.status), + onTap: () { + setState(() { + _pushMessageView(contact.onion); + }); + }, + ); + }, + ); + + final divided = ListTile.divideTiles( + context: context, + tiles: tiles, + ).toList(); + + return ListView(children: divided); + }, + ); + } + + void _pushMessageView(String handle) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return Provider( + create: (_) => Provider.of(context), + child: Scaffold( + appBar: AppBar( + title: Text('Convo: ' + widget.profileOnion), + ), + body: MessageView(profileOnion: widget.profileOnion, conversationHandle: handle), + )); + }, + ), + ); + } + +} diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart new file mode 100644 index 0000000..90e352a --- /dev/null +++ b/lib/views/messageview.dart @@ -0,0 +1,80 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../main.dart'; +import '../model.dart'; + +class MessageView extends StatefulWidget { + const MessageView({Key key, this.flwtch, this.profileOnion, this.conversationHandle}) : super(key: key); + final GlobalKey flwtch; + final String profileOnion; + final String conversationHandle; + + @override + _MessageViewState createState() => _MessageViewState(); +} + +class _MessageViewState extends State { + int conversationNumMessages = 0; + Timer timer; + + @override + Widget build(BuildContext context) { + return _buildMessageView(context); + } + + Widget _buildMessageView(BuildContext context) { + var numMessagesC = Provider.of(context).library.lookup>("NumMessages"); + // ignore: non_constant_identifier_names + final NumMessages = numMessagesC.asFunction(); + var getMessageC = Provider.of(context).library.lookup>("GetMessage"); + // ignore: non_constant_identifier_names + final GetMessage = getMessageC.asFunction(); + + _updateMessageCount(context, NumMessages); + timer = Timer.periodic(Duration(seconds: 1), (Timer t) => _updateMessageCount(context, NumMessages)); + + + return ProxyProvider0( + update: (_, __) => MessageCounter(conversationNumMessages), + child: ListView.builder( + itemCount: conversationNumMessages, + itemBuilder: (context, index) { + Pointer jsonMessageBytes = GetMessage( + Utf8.toUtf8(widget.profileOnion), widget.profileOnion.length, + Utf8.toUtf8(widget.conversationHandle), widget.conversationHandle.length, + index); + String jsonMessage = Utf8.fromUtf8(jsonMessageBytes); + //print(jsonMessage); + dynamic messageWrapper = jsonDecode(jsonMessage); + dynamic message = jsonDecode(messageWrapper['Message']); + + return ListTile( + title: Text(message['d']), + subtitle: Row( + children: [Text(messageWrapper['Timestamp']), messageWrapper['Acknowledged'] ? Icon(Icons.check_circle_outline) : Icon(Icons.hourglass_bottom_outlined)], + )); + }, + )); + } + + Future _updateMessageCount(BuildContext context, GetIntFromStrStrFn cfn) async { + setState(() { + conversationNumMessages = cfn( + Utf8.toUtf8(widget.profileOnion), widget.profileOnion.length, + Utf8.toUtf8(widget.conversationHandle), + widget.conversationHandle.length, + ); + }); + } +} + +class MessageCounter { + MessageCounter(this.x); + int x = 0; +} \ No newline at end of file diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart new file mode 100644 index 0000000..64c86de --- /dev/null +++ b/lib/views/profilemgrview.dart @@ -0,0 +1,107 @@ +import 'dart:convert'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../main.dart'; +import '../model.dart'; +import 'dart:collection'; + +import 'contactsview.dart'; + +class ProfileMgrView extends StatefulWidget { + @override + _ProfileMgrViewState createState() => _ProfileMgrViewState(); +} + +class _ProfileMgrViewState extends State { + HashMap _profiles; + + @override + Widget build(BuildContext context) { + if (_profiles == null) { + _profiles = new HashMap(); + } + + if (_profiles.length < 1) { + var getProfilesC = Provider.of(context).library.lookup>("GetProfiles"); + // ignore: non_constant_identifier_names + final GetProfiles = getProfilesC.asFunction(); + + Pointer jsonProfilesBytes = GetProfiles(); + String jsonProfiles = Utf8.fromUtf8(jsonProfilesBytes); + print(jsonProfiles); + + Map profiles = jsonDecode(jsonProfiles); + + profiles.forEach((onion,nick) { + ProfileModel profile1 = new ProfileModel(); + profile1.onion = onion; + profile1.nickname = nick; + profile1.creationDate = "4 jan 2020"; + profile1.contacts = new HashMap(); + + _profiles.putIfAbsent(profile1.onion, () => profile1); + }); + } + + return Scaffold ( + appBar: AppBar( + title: Text('Profiles'), + actions: [ + IconButton(icon: Icon(Icons.list), onPressed: () { },), + ], + ), + body: _buildProfileManager(),//_buildSuggestions(), + ); + } + + void _pushContactList(String profile) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return Provider( + create: (_) => Provider.of(context), + child: ContactsView(profileOnion: profile), + ); + }, + ), + ); + } + + String getNick(String profile, String contact) { + return contact == profile ? "me" : _profiles[profile].contacts[contact].nickname; + } + + Widget _buildProfileManager() { + final tiles = _profiles.values.map( + (ProfileModel profile) { + return ListTile( + title: Text( + profile.nickname, + style: Provider.of(context).biggerFont, + ), + subtitle: Text(profile.onion), + onTap: () { + setState(() { + var selectProfileC = Provider.of(context).library.lookup>("SelectProfile"); + // ignore: non_constant_identifier_names + final SelectProfile = selectProfileC.asFunction(); + + SelectProfile(Utf8.toUtf8(profile.onion), profile.onion.length); + _pushContactList(profile.onion); + }); + }, + ); + }, + ); + + final divided = ListTile.divideTiles( + context: context, + tiles: tiles, + ).toList(); + + return ListView(children: divided); + } +} + diff --git a/lib/widgets/torstatuslabel.dart b/lib/widgets/torstatuslabel.dart new file mode 100644 index 0000000..76c2960 --- /dev/null +++ b/lib/widgets/torstatuslabel.dart @@ -0,0 +1,43 @@ +import 'dart:ffi'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../main.dart'; +import '../model.dart'; + +class TorStatusLabel extends StatefulWidget { + @override + _TorStatusState createState() => _TorStatusState(); +} + +class _TorStatusState extends State { + String status = ""; + + @override + Widget build(BuildContext context) { + return Builder( + builder: (context2) => StreamBuilder( + stream: Provider.of(context).appStatus.torStatus(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return Text( + snapshot.hasData ? + snapshot.data : "Tor not yet Connected", + style: Theme + .of(context) + .textTheme + .headline4, + ); + }, + )); + } + + void _incrementCounter() { + setState(() { + var nextEventC = Provider.of(context).library.lookup>("NextEvent"); + // ignore: non_constant_identifier_names + final NextEvent = nextEventC.asFunction(); + NextEvent(); + }); + } +} diff --git a/pubspec.lock b/pubspec.lock index 04d45b8..e31c28e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -88,6 +88,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0-nullsafety.6" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4" path: dependency: transitive description: @@ -95,6 +102,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0-nullsafety.3" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.2+3" sky_engine: dependency: transitive description: flutter @@ -158,3 +172,4 @@ packages: version: "2.1.0-nullsafety.5" sdks: dart: ">=2.12.0-0.0 <3.0.0" + flutter: ">=1.16.0" diff --git a/pubspec.yaml b/pubspec.yaml index a0c7c6b..407f55e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,7 @@ environment: dependencies: flutter: sdk: flutter + provider: "4.3.2+3" # The following adds the Cupertino Icons font to your application.