blob: 7869dd663b71f990db79b0d0d95b41718d164c45 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import '../blocs/browser_bloc.dart';
import '../models/browse_action.dart';
const _kBackgroundColor = Colors.black;
const _kBackgroundFocusedColor = Color(0xFFFF8BCB);
const _kTextColor = Colors.white;
const _kTextFocusedColor = Colors.black;
enum _LayoutId { historyButtons, url }
class NavigationBar extends StatefulWidget {
final BrowserBloc bloc;
const NavigationBar({this.bloc});
@override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
FocusNode _focusNode;
TextEditingController _controller;
StreamSubscription _urlListener;
@override
void initState() {
_focusNode = FocusNode();
_controller = TextEditingController();
_urlListener = widget.bloc.url.listen((url) {
_controller.text = url;
});
super.initState();
}
@override
void dispose() {
_focusNode.dispose();
_controller.dispose();
_urlListener.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _focusNode,
builder: (_, child) {
final focused = _focusNode.hasFocus;
final textColor = focused ? _kTextFocusedColor : _kTextColor;
final bgColor = focused ? _kBackgroundFocusedColor : _kBackgroundColor;
return AnimatedTheme(
duration: Duration(milliseconds: 100),
data: ThemeData(
fontFamily: 'RobotoMono',
textSelectionColor: textColor.withOpacity(0.38),
textSelectionHandleColor: textColor,
hintColor: textColor,
cursorColor: textColor,
canvasColor: bgColor,
textTheme: TextTheme(
body1: TextStyle(color: textColor),
subhead: TextStyle(color: textColor),
),
),
child: child,
);
},
child: Material(
child: SizedBox(
height: 26.0,
child: _buildWidgets(),
),
),
);
}
Widget _buildWidgets() {
return CustomMultiChildLayout(
delegate: _LayoutDelegate(),
children: [
LayoutId(
id: _LayoutId.historyButtons,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildHistoryButton(
title: 'BCK',
onTap: () => widget.bloc.request.add(GoBackAction()),
enabledStateStream: widget.bloc.backState,
),
SizedBox(width: 8.0),
_buildHistoryButton(
title: 'FWD',
onTap: () => widget.bloc.request.add(GoForwardAction()),
enabledStateStream: widget.bloc.forwardState,
),
],
),
),
LayoutId(id: _LayoutId.url, child: _buildNavigationField()),
],
);
}
Widget _buildHistoryButton({
@required String title,
@required VoidCallback onTap,
@required Stream<bool> enabledStateStream,
}) {
return StreamBuilder<bool>(
stream: enabledStateStream,
initialData: false,
builder: (context, snapshot) {
final isEnabled = snapshot.data;
return GestureDetector(
onTap: isEnabled ? onTap : null,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Center(
child: Opacity(
opacity: isEnabled ? 1.0 : 0.54,
child: Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
),
),
),
);
},
);
}
Widget _buildNavigationField() {
return TextField(
focusNode: _focusNode,
controller: _controller,
cursorWidth: 7,
cursorRadius: Radius.zero,
cursorColor: Colors.black,
textAlign: TextAlign.center,
keyboardType: TextInputType.url,
style: TextStyle(fontSize: 14.0),
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
hintText: '<search>',
border: InputBorder.none,
isDense: true,
),
onSubmitted: (value) =>
widget.bloc.request.add(NavigateToAction(url: value)),
textInputAction: TextInputAction.go,
);
}
}
class _LayoutDelegate extends MultiChildLayoutDelegate {
@override
void performLayout(Size size) {
final buttonsSize = layoutChild(
_LayoutId.historyButtons,
BoxConstraints.tightFor(height: size.height),
);
positionChild(_LayoutId.historyButtons, Offset.zero);
final urlSize = layoutChild(
_LayoutId.url,
BoxConstraints.tightFor(width: size.width - buttonsSize.width * 2),
);
positionChild(
_LayoutId.url,
Offset(
(size.width - urlSize.width) * 0.5,
(size.height - urlSize.height) * 0.5,
),
);
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}