dual pane wiring

This commit is contained in:
erinn 2021-06-24 00:36:41 -07:00 committed by Sarah Jamie Lewis
parent eb12f57135
commit 82ee06d840
13 changed files with 255 additions and 134 deletions

View File

@ -136,7 +136,7 @@ class MainActivity: FlutterActivity() {
super.onResume()
Log.i("MainActivity.kt", "onResume")
if (myReceiver == null) {
Log.i("MainActivity.kt", "onResume registering localbroadcastreceiver")
Log.i("MainActivity.kt", "onResume registering local broadcast receiver / event bus forwarder")
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
myReceiver = MyBroadcastReceiver(mc)
@ -163,7 +163,7 @@ class MainActivity: FlutterActivity() {
override fun onDestroy() {
super.onDestroy()
Log.i("MainActivity.kt", "onDestroy")
Log.i("MainActivity.kt", "onDestroy - cancelling all WORKER_TAG and pruning old work")
WorkManager.getInstance(this).cancelAllWorkByTag(WORKER_TAG)
WorkManager.getInstance(this).pruneWork()
}
@ -188,6 +188,8 @@ class MainActivity: FlutterActivity() {
val Data = this.optString("Data")
}
// MainActivity.MyBroadcastReceiver receives events from the Cwtch service via im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS Android local broadcast intents
// then it forwards them to the flutter ui engine using the CWTCH_EVENTBUS methodchannel
class MyBroadcastReceiver(mc: MethodChannel) : BroadcastReceiver() {
val eventBus: MethodChannel = mc
@ -198,5 +200,4 @@ class MainActivity: FlutterActivity() {
eventBus.invokeMethod(evtType, evtData)
}
}
}

View File

@ -97,7 +97,9 @@ class CwtchNotifier {
break;
case "NewMessageFromPeer":
notificationManager.notify("New Message From Peer!");
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
break;
@ -122,7 +124,9 @@ class CwtchNotifier {
case "NewMessageFromGroup":
if (data["ProfileOnion"] != data["RemotePeer"]) {
//not from me
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
}
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++;
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
} else {

View File

@ -45,10 +45,6 @@ class Flwtch extends StatefulWidget {
class FlwtchState extends State<Flwtch> {
final TextStyle biggerFont = const TextStyle(fontSize: 18);
late Cwtch cwtch;
late ProfileInfoState selectedProfile;
String selectedConversation = "";
var columns = [1]; // default or 'single column' mode
//var columns = [1, 1, 2];
late ProfileListState profs;
final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler');
final MethodChannel shutdownMethodChannel = MethodChannel('im.cwtch.flwtch/shutdown');
@ -108,7 +104,7 @@ class FlwtchState extends State<Flwtch> {
supportedLocales: AppLocalizations.supportedLocales,
title: 'Cwtch',
theme: mkThemeData(settings),
home: appState.cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
home: appState.cwtchInit == true ? ShiftRightFixer(child: ProfileMgrView()) : SplashView(),
),
);
},
@ -128,24 +124,38 @@ class FlwtchState extends State<Flwtch> {
});
}
// Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt
// coder beware: args["RemotePeer"] is actually a handle, and could be eg a groupID
Future<void> _externalNotificationClicked(MethodCall call) async {
var args = jsonDecode(call.arguments);
var profile = profs.getProfile(args["ProfileOnion"])!;
var contact = profile.contactList.getContact(args["RemotePeer"])!;
contact.unreadMessages = 0;
navKey.currentState?.push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: contact),
],
builder: (context, child) => MessageView(),
);
},
),
);
// single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation
var isLandscape = Provider.of<AppState>(navKey.currentContext!, listen: false).isLandscape(navKey.currentContext!);
if (Provider.of<Settings>(navKey.currentContext!, listen: false).uiColumns(isLandscape).length == 1) {
if (navKey.currentContext?.findAncestorWidgetOfExactType<MessageView>() != null) {
print("messageview already open; popping before pushing replacement");
navKey.currentState?.pop();
}
navKey.currentState?.push(
MaterialPageRoute<void>(
builder: (BuildContext builderContext) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: profile),
ChangeNotifierProvider.value(value: contact),
],
builder: (context, child) => MessageView(),
);
},
),
);
} else { //dual pane
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["RemotePeer"];
}
}
@override

View File

@ -13,7 +13,7 @@ import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:glob/glob.dart';
import 'package:glob/list_local_fs.dart';
//import 'package:glob/list_local_fs.dart';
var globalSettings = Settings(Locale("en", ''), OpaqueDark());
var globalErrorHandler = ErrorHandler();

View File

@ -64,6 +64,8 @@ class ProfileListState extends ChangeNotifier {
class AppState extends ChangeNotifier {
bool cwtchInit = false;
String appError = "";
String? _selectedProfile;
String? _selectedConversation;
void SetCwtchInit() {
cwtchInit = true;
@ -74,6 +76,20 @@ class AppState extends ChangeNotifier {
appError = error;
notifyListeners();
}
String? get selectedProfile => _selectedProfile;
set selectedProfile(String? newVal) {
this._selectedProfile = newVal;
notifyListeners();
}
String? get selectedConversation => _selectedConversation;
set selectedConversation(String? newVal) {
this._selectedConversation = newVal;
notifyListeners();
}
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
}
class ContactListState extends ChangeNotifier {
@ -491,6 +507,8 @@ class MessageState extends ChangeNotifier {
}
set loaded(bool newVal) {
// quickly-arriving messages get discarded before loading sometimes
if (!hasListeners) return;
this._loaded = newVal;
notifyListeners();
}

View File

@ -9,6 +9,13 @@ import 'opaque.dart';
const TapirGroupsExperiment = "tapir-groups-experiment";
enum DualpaneMode {
Single,
Dual1to2,
Dual1to4,
CopyPortrait,
}
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
/// We also provide access to the version information here as it is also accessed from the
/// Settings Pane.
@ -19,8 +26,8 @@ class Settings extends ChangeNotifier {
// explicitly set experiments to false until told otherwise...
bool experimentsEnabled = false;
HashMap<String, bool> experiments = HashMap.identity();
String _uiColumnModePortrait = "Single";
String _uiColumnModeLandscape = "Same";
DualpaneMode _uiColumnModePortrait = DualpaneMode.Single;
DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait;
bool blockUnknownConnections = false;
@ -75,6 +82,10 @@ class Settings extends ChangeNotifier {
// Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON
experiments = new HashMap<String, bool>.from(settings["Experiments"]);
// single pane vs dual pane preferences
_uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]);
_uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]);
// Push the experimental settings to Consumers of Settings
notifyListeners();
}
@ -136,18 +147,55 @@ class Settings extends ChangeNotifier {
notifyListeners();
}
String get uiColumnModePortrait => _uiColumnModePortrait;
set uiColumnModePortrait(String newval) {
DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait;
set uiColumnModePortrait(DualpaneMode newval) {
this._uiColumnModePortrait = newval;
notifyListeners();
}
String get uiColumnModeLandscape => _uiColumnModeLandscape;
set uiColumnModeLandscape(String newval) {
DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape;
set uiColumnModeLandscape(DualpaneMode newval) {
this._uiColumnModeLandscape = newval;
notifyListeners();
}
List<int> uiColumns(bool isLandscape) {
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
switch(m) {
case DualpaneMode.Single: return [1];
case DualpaneMode.Dual1to2: return [1, 2];
case DualpaneMode.Dual1to4: return [1, 4];
}
print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape");
return [1];
}
static List<DualpaneMode> uiColumnModeOptions(bool isLandscape) {
if (isLandscape) return [DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4,];
else return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4];
}
static DualpaneMode uiColumnModeFromString(String m) {
switch(m) {
case "DualpaneMode.Single": return DualpaneMode.Single;
case "DualpaneMode.Dual1to2": return DualpaneMode.Dual1to2;
case "DualpaneMode.Dual1to4": return DualpaneMode.Dual1to4;
case "DualpaneMode.CopyPortrait": return DualpaneMode.CopyPortrait;
}
print("Error: ui requested translation of column mode [$m] which doesn't exist");
return DualpaneMode.Single;
}
static String uiColumnModeToString(DualpaneMode m) {
// todo: translate
switch(m) {
case DualpaneMode.Single: return "Single";
case DualpaneMode.Dual1to2: return "Double (1:2)";
case DualpaneMode.Dual1to4: return "Double (1:4)";
case DualpaneMode.CopyPortrait: return "Same as portrait mode setting";
}
}
/// Construct a default settings object.
Settings(this.locale, this.theme);
@ -164,7 +212,9 @@ class Settings extends ChangeNotifier {
"ExperimentsEnabled": this.experimentsEnabled,
"Experiments": experiments,
"StateRootPane": 0,
"FirstTime": false
"FirstTime": false,
"UIColumnModePortrait": uiColumnModePortrait.toString(),
"UIColumnModeLandscape": uiColumnModeLandscape.toString(),
};
}
}

View File

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
import '../settings.dart';
import 'contactsview.dart';
import 'messageview.dart';
@ -14,24 +15,25 @@ class DoubleColumnView extends StatefulWidget {
class _DoubleColumnViewState extends State<DoubleColumnView> {
@override
Widget build(BuildContext context) {
var flwtch = Provider.of<FlwtchState>(context);
var flwtch = Provider.of<AppState>(context);
var cols = Provider.of<Settings>(context).uiColumns(true);
return Flex(
direction: Axis.horizontal,
children: <Widget>[
Flexible(
flex: flwtch.columns[0],
flex: cols[0],
child: ContactsView(
key: widget.key,
),
),
Flexible(
flex: flwtch.columns[1],
child: flwtch.selectedConversation == ""
? Center(child: Text(AppLocalizations.of(context)!.addContactFirst))
flex: cols[1],
child: flwtch.selectedConversation == null
? Card(child:Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))
: //dev
MultiProvider(providers: [
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation)!),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation!)!),
], child: Container(child: MessageView())),
),
],

View File

@ -80,24 +80,33 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor()),
),
ListTile(
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())),
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Portrait Mode", style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
trailing: DropdownButton(
value: "Single",
value: settings.uiColumnModePortrait.toString(),
onChanged: (String? newValue) {
if (newValue == "Double (1:2)") {
Provider.of<FlwtchState>(context).columns = [1, 2];
} else if (newValue == "Double (1:4)") {
Provider.of<FlwtchState>(context).columns = [1, 4];
} else {
Provider.of<FlwtchState>(context).columns = [1];
}
settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!);
saveSettings(context);
},
// TODO: Only allow in landscape?
items: (Platform.isAndroid ? ["Single"] : ["Single", "Double (1:2)", "Double (1:4)"]).map<DropdownMenuItem<String>>((String value) {
items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
value: value.toString(),
child: Text(Settings.uiColumnModeToString(value)),
);
}).toList())),
ListTile(
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Landscape Mode", style: TextStyle(color: settings.current().mainTextColor())),
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
trailing: DropdownButton(
value: settings.uiColumnModeLandscape.toString(),
onChanged: (String? newValue) {
settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
saveSettings(context);
},
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(Settings.uiColumnModeToString(value)),
);
}).toList())),
SwitchListTile(

View File

@ -42,10 +42,13 @@ class _MessageViewState extends State<MessageView> {
@override
Widget build(BuildContext context) {
var appState = Provider.of<AppState>(context);
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
// setting leading to null makes it do the default behaviour; container() hides it
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
title: Text(Provider.of<ContactInfoState>(context).nickname),
actions: [
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),

View File

@ -42,41 +42,45 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Prevents Android back button from closing the app on the profile manager screen
// (which would shutdown connections and all kinds of other expensive to generate things)
// TODO pop up a dialogue regarding closing the app?
builder: (context, settings, child) => WillPopScope(
onWillPop: () async {
_showShutdown();
return closeApp;
},
child: Scaffold(
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
title: Row(children: [
Image(
image: AssetImage("assets/core/knott-white.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 32,
height: 32,
colorBlendMode: BlendMode.dstIn,
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
builder: (context, settings, child) =>
WillPopScope(
onWillPop: () async {
_showShutdown();
return closeApp;
},
child: Scaffold(
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
title: Row(children: [
Image(
image: AssetImage("assets/core/knott-white.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 32,
height: 32,
colorBlendMode: BlendMode.dstIn,
color: Provider
.of<Settings>(context)
.theme
.backgroundHilightElementColor(),
),
SizedBox(
width: 10,
),
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
]),
actions: getActions(),
),
SizedBox(
width: 10,
floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile,
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
child: Icon(
Icons.add,
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
),
),
Expanded(child: Text(AppLocalizations.of(context)!.titleManageProfiles, style: TextStyle(color: settings.current().mainTextColor())))
]),
actions: getActions(),
),
floatingActionButton: FloatingActionButton(
onPressed: _pushAddEditProfile,
tooltip: AppLocalizations.of(context)!.addNewProfileBtn,
child: Icon(
Icons.add,
semanticLabel: AppLocalizations.of(context)!.addNewProfileBtn,
),
),
body: _buildProfileManager(),
)),
body: _buildProfileManager(),
)),
);
}

View File

@ -3,9 +3,12 @@ import 'package:cwtch/views/profilemgrview.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../model.dart';
import '../settings.dart';
import 'contactsview.dart';
import 'messageview.dart';
// currently unused but maybe one day?
class TripleColumnView extends StatefulWidget {
@override
_TripleColumnViewState createState() => _TripleColumnViewState();
@ -14,19 +17,22 @@ class TripleColumnView extends StatefulWidget {
class _TripleColumnViewState extends State<TripleColumnView> {
@override
Widget build(BuildContext context) {
var flwtch = Provider.of<FlwtchState>(context);
var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context);
var columns = settings.uiColumns(appState.isLandscape(context));
return Flex(direction: Axis.horizontal, children: <Widget>[
Flexible(
flex: flwtch.columns[0],
flex: columns[0],
child: ProfileMgrView(),
),
Flexible(
flex: flwtch.columns[1],
child: flwtch.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev
flex: columns[1],
child: appState.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev
),
Flexible(
flex: flwtch.columns[2],
child: flwtch.selectedConversation == ""
flex: columns[2],
child: appState.selectedConversation == null
? Center(child: Text(AppLocalizations.of(context)!.addContactFirst))
: //dev
Container(child: MessageView()),

View File

@ -80,17 +80,19 @@ class _ContactRowState extends State<ContactRow> {
]),
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);
flwtch.setState(() => flwtch.selectedConversation = contact.onion);
// case 2/3 handled by Double/TripleColumnView respectively
if (flwtch.columns.length == 1) _pushMessageView(contact.onion);
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
// if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
});
},
));
}
void _pushMessageView(String handle) {
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
Navigator.of(context).push(
MaterialPageRoute<void>(

View File

@ -30,29 +30,43 @@ class _ProfileRowState extends State<ProfileRow> {
padding: const EdgeInsets.all(2.0), //border size
child: ProfileImage(
badgeCount: 0,
badgeColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor(),
badgeTextColor: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor(),
badgeColor: Provider
.of<Settings>(context)
.theme
.portraitProfileBadgeColor(),
badgeTextColor: Provider
.of<Settings>(context)
.theme
.portraitProfileBadgeTextColor(),
diameter: 64.0,
imagePath: profile.imagePath,
border: profile.isOnline ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor())),
border: profile.isOnline ? Provider
.of<Settings>(context)
.theme
.portraitOnlineBorderColor() : Provider
.of<Settings>(context)
.theme
.portraitOfflineBorderColor())),
Expanded(
child: Column(
children: [
Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
ExcludeSemantics(
child: Text(
profile.onion,
softWrap: true,
overflow: TextOverflow.ellipsis,
))
],
)),
children: [
Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider
.of<FlwtchState>(context)
.biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
ExcludeSemantics(
child: Text(
profile.onion,
softWrap: true,
overflow: TextOverflow.ellipsis,
))
],
)),
IconButton(
enableFeedback: true,
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
@ -65,37 +79,35 @@ class _ProfileRowState extends State<ProfileRow> {
),
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);
flwtch.setState(() {
flwtch.selectedProfile = profile;
flwtch.selectedConversation = "";
});
var appState = Provider.of<AppState>(context, listen: false);
appState.selectedProfile = profile.onion;
appState.selectedConversation = null;
switch (flwtch.columns.length) {
case 1:
_pushContactList(profile, false);
break;
case 2:
_pushContactList(profile, true);
break;
} // case 3: handled by TripleColumnView
_pushContactList(profile, appState.isLandscape(context));//orientation == Orientation.landscape);
});
},
));
}
void _pushContactList(ProfileInfoState profile, bool includeDoublePane) {
void _pushContactList(ProfileInfoState profile, bool isLandscape) {
Navigator.of(context).push(
MaterialPageRoute<void>(
settings: RouteSettings(name: "conversations"),
builder: (BuildContext buildcontext) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
],
builder: (context, widget) => includeDoublePane ? DoubleColumnView() : ContactsView(),
);
return OrientationBuilder(
builder: (orientationBuilderContext, orientation) {
return MultiProvider(
providers: [
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
],
builder: (innercontext, widget) {
var appState = Provider.of<AppState>(context);
var settings = Provider.of<Settings>(context);
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView();
}
);
});
},
),
);